Pieniä kysymyksiä ohjelmoinnista

Totta, kiitos paljon! Huomaa että tarvitse enemmän kokemusta SQL:stä :)
Paitsi ettei tämä sittenkään toimi:
(TUOTE.HINTA = TAULU.MIN)
OR
(TUOTE.HINTA > TAULU.MIN
and TUOTE.HINTA <= TAULU.MAX)
Jos hinta = 10, kysely palauttaa molemmat rivit.
TUOTE MIN MAX
EKA 5 10
TOKA 10 15
 
Paitsi ettei tämä sittenkään toimi:
(TUOTE.HINTA = TAULU.MIN)
OR
(TUOTE.HINTA > TAULU.MIN
and TUOTE.HINTA <= TAULU.MAX)
Jos hinta = 10, kysely palauttaa molemmat rivit.
TUOTE MIN MAX
EKA 5 10
TOKA 10 15

Jos EKA ja TOKA ovat ryhmiä jotka määritellään tuotteelle hinnan perusteella niin silloin kannattaa asettaa rajat niin että eivä t mene päällekkäin.

Jos kuitenkin menee niin min < hinta and hinta <= max pitäisi tuottaa oikea tulos.

Ongelma voi tulla myös siitä jos luvut ovat desimaaleja eikä kokonaisia...
 
Jeps, jos nuo hinnat on kokonaislukuja niin mikä kieltää ekan rajan laskemista alemmas? laittaa ekan 4-10 niin saa 5-10 toimimaan.
 
SQL ongelma. Eli mulla on käsissä tyhmästi tehty taulu tähän tyyliin:
TUOTE MIN MAX
EKA 5 10
TOKA 10 15
ja tuossa rajalla katsotaan että arvon 10 tuote kuuluu luokkaan "EKA".
Haen taulusta arvoja näin vertailemalla:
TUOTE.HINTA > TAULU.MIN
and TUOTE.HINTA <= TAULU.MAX
Homma toimii hienosti muuten, mutta aivan alin arvo eli "5" jää pois. Miten tuon saa järkevimmin tehtyä?

Kaksi järkevää ratkaisua:

1. Muuta taulua niin, ettei se sisällä yhteisiä raja-arvoja.
2. Muuta UI:ta niin, että siinä kerrotaan selkeästi toisen arvoista olevan lähestyttävä raja-arvo (esim. Hinta alle 10), ja käytä alkuperäistä kyselyä.

Mikäli kyseessä on legacy, johon et voi vaikuttaa, joutuu tekemään kikkoja - ja kikat luonnollisesti hidastavat kyselyitä.

Jos tämä on kuitenkin esim. vain yhdelle tuotteelle tehtävä asia, jota ei ajeta massoissa, usein ja kriittisesti, voidaan kantaa vasten ajaa tehoa syövää mörköä:

Koodi:
SELECT * FROM (
  SELECT
    TUOTE.ID,
    TUOTE.HINTA,
    TAULU.TUOTE
  FROM TUOTE
  JOIN TAULU ON
    (TUOTE.HINTA >= TAULU.MIN AND TUOTE.HINTA <= TAULU.MAX)
  ORDER BY TAULU.MAX ASC
) AS t
GROUP BY t.ID

Käytössä luultavimmin on Mysli, jolloin en suosittele tätä ~ikinä. Ulkoqueryn group by tekee homman _luultavasti aina_ temppitaulun kautta filesortilla, ja hidastuu vähänkään isommalla massalla typerän paljon.
 
Kiitos vinkeistä, mutta niin kuin alustuksessa kerroin "mulla on käsissä tyhmästi tehty taulu" eli sisältöä en pääse muuttamaan. Eihän tuossa rakenteessa ole mitään järkeä missään mielessä.
Haen dataa kyselyllä eli kyseessä ei ole UI. Kyselyn muut taulut ovat aika isoja, esimerkiksi yhdessä taulussa ~37 miljoonaa riviä ja toisessa ~3 miljoonaa riviä.
Jatkanpa tuumailua mitä tuon kanssa tekisi..
 
Mistähän syystä tuo Context.Users.Add ei toimi? Kokeilin myös ilman asyncia. Koodi menee tuosta läpi kyllä, mutta mitään muutosta ei tapahdu tuolla tietokannassa..?

upload_2019-8-27_20-6-31.png


Tässä vielä tuo DbContext:
upload_2019-8-27_20-7-57.png


EDIT: Auttoi, kun vaihdoin tuon "using (Context)" => "using (var c= new DatabaseContext())". Miksiköhän ihmeessä? Eikö tuo Context palauta aina uuden DatabaseContext:n, kun siellä (luokassa minkä tuo perii) lukee:

"Context => new DatabaseContext();"
 
Viimeksi muokattu:
Alkuvuodesta löysin kokonaisen sivuston, joka käsitteli nettipalveluiden turvallisuutta kehittäjän näkökulmasta. Siellä oli mm. tarkistuslistoja. Ei tullut laitettua kirjanmerkkeihin ja nyt en enää löydä sitä :facepalm: Tietääkö kukaan mistä sivustosta mahtoi olla kyse?
 
Alkuvuodesta löysin kokonaisen sivuston, joka käsitteli nettipalveluiden turvallisuutta kehittäjän näkökulmasta. Siellä oli mm. tarkistuslistoja. Ei tullut laitettua kirjanmerkkeihin ja nyt en enää löydä sitä :facepalm: Tietääkö kukaan mistä sivustosta mahtoi olla kyse?

OWASP?
 
Mistähän syystä tuo Context.Users.Add ei toimi? Kokeilin myös ilman asyncia. Koodi menee tuosta läpi kyllä, mutta mitään muutosta ei tapahdu tuolla tietokannassa..?

upload_2019-8-27_20-6-31.png


Tässä vielä tuo DbContext:
upload_2019-8-27_20-7-57.png


EDIT: Auttoi, kun vaihdoin tuon "using (Context)" => "using (var c= new DatabaseContext())". Miksiköhän ihmeessä? Eikö tuo Context palauta aina uuden DatabaseContext:n, kun siellä (luokassa minkä tuo perii) lukee:

"Context => new DatabaseContext();"

Oletettavasti täältä löytyy vastaus: using statement - C# Reference

"The using statement defines a scope at the end of which an object will be disposed"

"You can instantiate the resource object and then pass the variable to the using statement, but this is not a best practice. In this case, after control leaves the using block, the object remains in scope but probably has no access to its unmanaged resources. In other words, it's not fully initialized anymore. If you try to use the object outside the using block, you risk causing an exception to be thrown. For this reason, it's generally better to instantiate the object in the using statement and limit its scope to the using block."


Best practise on käyttää using(new Jotain(), new Toinen) jolloin objektit heitetään automaattisesti menemään using(...) { ... } jälkeen

Voi siis olla että globaalilla dbcontextilla ensimmäinen kerta onnistuu ja muut ei koska resurssit häviää alta.
 
EDIT: Auttoi, kun vaihdoin tuon "using (Context)" => "using (var c= new DatabaseContext())". Miksiköhän ihmeessä? Eikö tuo Context palauta aina uuden DatabaseContext:n, kun siellä (luokassa minkä tuo perii) lukee:

"Context => new DatabaseContext();"

Juuri noin kuin ississ jo vastasikin. Toi using tyhjentää lopuksi sen contextin. Toinen yrityksesi toimii koska siinä se luodaan aina uudestaan ennen käyttöä. Todennäköisesti tuossa ei kannattaisi käyttää usingia lainkaan, voit käyttää suoraan sitä alkuperäistä Contextia minkä loit.

Do I always have to call Dispose() on my DbContext objects? Nope
 
Juuri noin kuin ississ jo vastasikin. Toi using tyhjentää lopuksi sen contextin. Toinen yrityksesi toimii koska siinä se luodaan aina uudestaan ennen käyttöä. Todennäköisesti tuossa ei kannattaisi käyttää usingia lainkaan, voit käyttää suoraan sitä alkuperäistä Contextia minkä loit.

Do I always have to call Dispose() on my DbContext objects? Nope
Hmm, otin nuo using-blokit pois ja laitoin vaan pääluokkaan tuon "public DatabaseContext Context => new DatabaseContext();" ja sitten funktioihin suoraan Context:n käytön. Tuli sama virhe kuin aiemmin sillä using (Context) blokilla, eli Add ja Update eivät tee mitään.
 
Pahoitteluni jos menee väärälle osiolle tai väärään ketjuun:

Koodi:
for /f "delims=: tokens=2" %%a in ('ipconfig ^| findstr /R /C:"IPv4 Address"') do (set tempip=%%a) 
set tempip=%tempip: =% 
echo %tempip%

Mitä tuo set tempip=%tempip: =% tekee? Tai siis se poistaa välilyönnin ip-osoitteen edestä, sen tiedän mutta miten se tekee sen? Googlella en löytänyt ratkaisua.

Kiitos jo etukäteen vastauksesta.
 
Pahoitteluni jos menee väärälle osiolle tai väärään ketjuun:

Koodi:
for /f "delims=: tokens=2" %%a in ('ipconfig ^| findstr /R /C:"IPv4 Address"') do (set tempip=%%a)
set tempip=%tempip: =%
echo %tempip%

Mitä tuo set tempip=%tempip: =% tekee? Tai siis se poistaa välilyönnin ip-osoitteen edestä, sen tiedän mutta miten se tekee sen? Googlella en löytänyt ratkaisua.

Kiitos jo etukäteen vastauksesta.
Tuolta löytynee vastaus tähän syntaksiin:
CMD Variable edit replace - Windows CMD - SS64.com

Aukeaako? :)
 
Hmm, otin nuo using-blokit pois ja laitoin vaan pääluokkaan tuon "public DatabaseContext Context => new DatabaseContext();" ja sitten funktioihin suoraan Context:n käytön. Tuli sama virhe kuin aiemmin sillä using (Context) blokilla, eli Add ja Update eivät tee mitään.

Jokaisella kutsulla tuo property palauttaa uuden instanssin tuosta dbcontext luokasta. Ja kun tuota suhteuttaa tuohon alkuperäiseen koodinpätkään minkä aiemmassa postissa laitoit niin mitään ei luonnollisesti tapahdu, kun SaveChanges:ia kutsutaan, siinä kyseisessä instanssissa ei ole mitään muutoksia mitä tallentaa, muutokset on siinä edellisessä instanssissa.

Suosittelen ottamaan käyttöön jonkun DI-kikkareen (esim. se (ASP).NET Core oma totetutus toimii tähän tarpeeseen loistavasti), joka periaatteessa sitten myös huolehtii siitä, että uusi dbcontext instanssi a) injektoidaan esim. per request tuolle luokalle b) disposaa sen kun request on käsitelty.
 
Viimeksi muokattu:
Tää on ehkä liian tyhmä kysymys, mutta menkööt.
Mun pitäisi saada windows 10:n käynnistyessä ajettua komentokehotteella tietyssä folderissa komento: npm run start

Ilmeisesti pitäisi tehdä -bat filu, jonka vie windowsin käynnistyksen yhteydessä startattaviin ohjelmiin.

Mutta mikä tuon batch-filun sisältö pitäisi olla, jotta toiveeni toteutuisi?
 
Tää on ehkä liian tyhmä kysymys, mutta menkööt.
Mun pitäisi saada windows 10:n käynnistyessä ajettua komentokehotteella tietyssä folderissa komento: npm run start

Ilmeisesti pitäisi tehdä -bat filu, jonka vie windowsin käynnistyksen yhteydessä startattaviin ohjelmiin.

Mutta mikä tuon batch-filun sisältö pitäisi olla, jotta toiveeni toteutuisi?
npm run start

Vie se sitten siihen kansioon missä haluat ajaa sen komennon ja tee pikakuvake käynnistettäviin ohjelmiin

kun teet tekstieditorilla niin tallenna lainausmerkeissä "npmstart.bat" niin notepad ei tee siitä npmstart.bat.txt
 
Joissain tapauksissa parempi vaihtoehto voi olla ajaa sovellus(ta) Windows-palveluna. Hakukoneeseen how to run(/install) node app as windows service..
Jos tarkoitus on ajaa yksi ainoa komento kertaalleen koneen käynnistyksen yhteydessä, niin en kyllä ihan heti keksi miksi siitä kannattaisi tehdä yhtään monimutkaisempaa kikkailemalla se palveluksi.
 
okei. Eli käytännössä tuo TheMell:n postaus olisi meikäläisen pelastus.

"npm run start

Vie se sitten siihen kansioon missä haluat ajaa sen komennon ja tee pikakuvake käynnistettäviin ohjelmiin

kun teet tekstieditorilla niin tallenna lainausmerkeissä "npmstart.bat" niin notepad ei tee siitä npmstart.bat.txt"

Siis batin sisällä ei tarvitse olla mitään muuta kuin tuo npm run start komento? Olen dorka jos ja kun en tuota yksinkertaisuutta ymmärtänyt. Ja totta, jos se on siellä kansiossa niin siellähän se sitten ajetaan.
 
okei. Eli käytännössä tuo TheMell:n postaus olisi meikäläisen pelastus.

"npm run start

Vie se sitten siihen kansioon missä haluat ajaa sen komennon ja tee pikakuvake käynnistettäviin ohjelmiin

kun teet tekstieditorilla niin tallenna lainausmerkeissä "npmstart.bat" niin notepad ei tee siitä npmstart.bat.txt"

Siis batin sisällä ei tarvitse olla mitään muuta kuin tuo npm run start komento? Olen dorka jos ja kun en tuota yksinkertaisuutta ymmärtänyt. Ja totta, jos se on siellä kansiossa niin siellähän se sitten ajetaan.
Ei siellä batissa tarvitse olla muuta, mutta yleensä alussa laitetaan komentojen kaiutus pois päältä, eli tähän tapaan:

Koodi:
@echo off
npm run start

Sitten vaan tallennat tuon .bat -päätteisenä kansioon, josta sitä haluat ajaa ja lisäät sille pikakuvakkeen starttivalikoon (start --> run --> "shell:startup"). Toinen vaihtoehto olisi luoda järjestelmään scheduled task (ajastettu tehtävä) ja määritellä se suoritettavaksi aina bootin yhteydessä. Lopputulos on sama.

Itse tekisin tämän powershell-sktiptinä ja käynnistys ajastettuna tehtävänä, mutta ei mennä tässä siihen.
 
Noniin. Jatkokysymys jo aiemmin aloitetulle batin tekemiselle, jossa ratkaisu oli siis luoda bat-tiedosto joka ajaa asiansa ja on nyt:

@echo off
npm run start

Milläs ilveellä tuon npm run startin joka avaa erilisenn komentokehotteen auki, saa minimoitua? Nyt prompti aukeaa suoraan näytölle muun oleellisen päälle, joten pitäisi saada jätettyä auki mutta pienennettyä alapalkkiin.
Sieppaa.PNG


Edit: lisäsin vielä kuvan, tuollaiset aukeaa kun npm run startin ajaa. Mietin, että pitääkö määritellä jokin näistä aukeamaan mininä?
 
Viimeksi muokattu:
Ehdotus 2, ehkä vähän hooceempi käyttötapauksesta riippuen: Käytä dockeria. Docker taitaa asentamisen yhteydessä käynnistää itsensä automaattisesti Windowsin yhteydessä. Teet sovelluksestasi docker-imagen, ja käynnistät docker-containerin lisävivulla --restart unless-stopped , jolloin kontti käynnistetään dockerin yhteydessä ja käynnistetään uudestaan myös esimerkiksi softan kaatuessa. ;)
 
Noniin. Jatkokysymys jo aiemmin aloitetulle batin tekemiselle, jossa ratkaisu oli siis luoda bat-tiedosto joka ajaa asiansa ja on nyt:

@echo off
npm run start

Milläs ilveellä tuon npm run startin joka avaa erilisenn komentokehotteen auki, saa minimoitua? Nyt prompti aukeaa suoraan näytölle muun oleellisen päälle, joten pitäisi saada jätettyä auki mutta pienennettyä alapalkkiin.

Jos (ja ilmeisesti kun) kyseessä on Windows, fiksuin tapa lienee päästää bat vapaaksi helvetistä, ja ottaa käyttöön VBScript:

1. Luo esim. "start.vbs"-nimellä tiedosto
2. Lisää tiedostoon seuraava koodi:
Koodi:
CreateObject("Wscript.Shell").Run "npm run start", 0
3. ?
4. Profit

(huomiona, että tässä koko komentokehote jää aukeamatta näkyviin - mikäli haluat jättää esim. virhetilanteita varten ikkunan näkyviin, vaihda nollan (0) tilalle kaksi (2))
 
Heps kukkuu. @wex , eli tuo 0 jättää kokonaan aukeamatta ruutuun. Tavallaan hyvä, mutta voisi olla näppärämpi tässä skenaariossa että olisi minimoitu vain alapalkkiin. Onko tuohon jokin vaihtoehto. Kyseessä tosiaan win10, ja komentokehotteessa pyörii google assistantin loki sitä mukaa kun jotain tapahtuu tai ei tapahdu. Näppärää olisi, että jos assistant menee mykäksi niin ikkunaa maksimoimalla näkisi mikä error ruudussa on. Muutoin ikkuna saisi olla tosiaan alapalkissa jemmassa, koska lokia ei tarvitse kovinkaan usein käydä vilkuilemassa.
 
Heps kukkuu. @wex , eli tuo 0 jättää kokonaan aukeamatta ruutuun. Tavallaan hyvä, mutta voisi olla näppärämpi tässä skenaariossa että olisi minimoitu vain alapalkkiin. Onko tuohon jokin vaihtoehto. Kyseessä tosiaan win10, ja komentokehotteessa pyörii google assistantin loki sitä mukaa kun jotain tapahtuu tai ei tapahdu. Näppärää olisi, että jos assistant menee mykäksi niin ikkunaa maksimoimalla näkisi mikä error ruudussa on. Muutoin ikkuna saisi olla tosiaan alapalkissa jemmassa, koska lokia ei tarvitse kovinkaan usein käydä vilkuilemassa.

Ihan viestin lopussa olikin sivuhuomiona, että ikkuna on kokonaan piilossa - ja vaihtamalla nollan tilalle kakkosen toimii halutulla tavalla ;)
 
Ihan viestin lopussa olikin sivuhuomiona, että ikkuna on kokonaan piilossa - ja vaihtamalla nollan tilalle kakkosen toimii halutulla tavalla ;)

Juu mä en ymmärtänyt, enkä tiedä ymmärsinkö edelleenkään että jääkö se ikkuna auki, alapalkkiin, vai kokonaan aukeamatta noilla numerovaihtoehdoilla. Mä kuitenkin viisastun kun tuossa alan testailemaan :) Kiitos jo etukäteen.
 
Kysymys djangosta, tai siis sen loggerista.


Koodi:
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': './debug.log',
        },
    },
    'loggers': {
        'django.db.backends':{
            'handlers': ['file'],
            'level': 'DEBUG',
        }
    },
}

onko tämän login jokainen rivi ( sql kysely ) oikeasti lähetenyt kantapannulle asti?
 
Koodi:
import React from "react";
import "./Form.css";

const Form = props => {
  return (
    <form onSubmit={props.createNewItem} className="input-form">
      <input type="text" name="name" placeholder="name" required />
      <input
        type="text"
        name="description"
        placeholder="description"
        required
      />
      <input type="text" name="comment" placeholder="comment <optional>" />
      <div className="form-btns">
        <button
          onClick={props.clearFields}
          className="form-btn btn-disabled"
          id="clear-btn"
        >
          Clear
        </button>
        <button className="form-btn">Add</button>
      </div>
    </form>
  );
};

export default Form;

Reactin harjoittelua, mutta itse kysymykseen. Miten tuollaisessa funktionaalisessa komponentissa saa inputin valuen selville?
Tarkoituksena muuttaa napin luokkaa riippuen onko kentät tyhjiä vai ei.
 
Reactin harjoittelua, mutta itse kysymykseen. Miten tuollaisessa funktionaalisessa komponentissa saa inputin valuen selville?
Tarkoituksena muuttaa napin luokkaa riippuen onko kentät tyhjiä vai ei.

Joko:

1. Joka kerta kun inputin tila muuttuu, kutsut funktiota (jonka välität propsien kautta), joka muuttaa isäntäkomponentin/tilakoneen tilaa. Ja se tila taas välitetään myös propsin kautta, jolloin input näyttää oikein sisällön.

tai

2. Teet tästä tilallisen komponentin esim. uusilla hookseilla, joilla homma menee nätistä. Silloin tämä komponentti on tilallinen ja itse hoitaa tilan päivittämisen ja siitä lukemisen.

Tai vanhalla luokkatyylillä:

Forms – React

Tilan perusteella voit sitten puukottaa luokan haluamaksesi.
 
Reactiin ja ylläolevaan kyssäriin liittyen, onko sillä mitään käytännön eroa onko se props suluissa vai ei? Vai onko se tietyissä tilanteissa? Kummastelen vaa ku oon törmännyt muutaman kerran muiden koodissa.

Koodi:
const Form = (props) => {}..
vs.
Koodi:
const Form = props => {}..
 
Reactiin ja ylläolevaan kyssäriin liittyen, onko sillä mitään käytännön eroa onko se props suluissa vai ei? Vai onko se tietyissä tilanteissa? Kummastelen vaa ku oon törmännyt muutaman kerran muiden koodissa.

Koodi:
const Form = (props) => {}..
vs.
Koodi:
const Form = props => {}..
Tuo ei liity mitenkään oikeastaan Reactiin, vaan JavaScriptiin. Nuolisyntaksia käytettäessä, jos funktiolla on vain yksi argumentti, niin sulkuja ei tarvita, muissa tapauksissa tarvitaan.
 
@qweeqwqwe Sulut on pakolliset jos funktioparametreja on useita tai niitä ei ole lainkaan, mutta muuten ne voi jättää halutessaan pois.

Enemmän koodaavat ja isommat projektit noudattavat yleensä jonkinlaista linttaussäännöstöä (esim. eslint + airbnb), joka pakottaa nuo tiettyyn muotoon.

Koska propsit on objekti, on yleensä myös hyvä tapa destrukturoida se, jolloin välttyy siltä jatkuvalta objekti.objektinAttribuutti toistolta.
Siis esim.

Koodi:
// huom sulut pakolliset, vaikka parametrejä on "yksi"

const Form = ({ handleChange, handleSubmit }) => {

  // ....
  <input onChange={handleChange} ... />
}

// vs.

const Form = props => {

  // ....
  <input onChange={props.handleChange} ... />
}

Sulkujen ja muiden tarve tai tarpeettomuus voi näkyä tyylivalinnoissa myös versiohallinnan vuoksi. Esim.javascript hyväksyy tyylin, jossa objektin attribuutteja tai listan alkioita lueteltaessa viimeisen jälkeen saa tulla tai olla tulematta pilkku. Usein tyyliohjeet valitsevat ylimääräisen pilkun, koska versiohallinnassa pilkun lisääminen ja rivin lisääminen näkyy kahtena muutoksena, mutta jos rivillä on aina pilkku, riittää yhden rivin lisääminen ja siksi se näkyy vain rivin muutoksena.

Siis esim.

Koodi:
// jos tähän lisää uuden, versionhallinta highlightaa vaan lisätyn rivin, versus lisätyn + pilkun lisäämisen

const states = [
   'OPEN',
   'CLOSED',
   'ERROR',
]

...tai se, käytetäänkö merkkijonoissa "" vai ''. Yleensä '', koska on tyypillisempää, että merkkijono sisältää lainausmerkin kuin hipsun.

Jälleen

Koodi:
const string = 'Marko sanoi: "Hepsan hei"'

// vs.

const string = "Marko sanoi: \"Hepsan hei\""
 
Viimeksi muokattu:
@qweeqwqwe, tuossa tapauksessa ei ole väliä. Joskus sulkeet tarvitaan, kuten vaikka jos on useita parametreja tai jos haluaa destruktoida objektin parametreissä:

Koodi:
const foo = ({a, b}) => {...}

Tai esim. TypseScript vaatii sulkeita kun parametreille annetaan tyypit.
 
Reactiin ja ylläolevaan kyssäriin liittyen, onko sillä mitään käytännön eroa onko se props suluissa vai ei? Vai onko se tietyissä tilanteissa? Kummastelen vaa ku oon törmännyt muutaman kerran muiden koodissa.

Koodi:
const Form = (props) => {}..
vs.
Koodi:
const Form = props => {}..

Tässä on tullutkin jo hyviä vastauksia, mutta kannan oman korteni kekoon.

Lyhyt vastaus: ei ole eroa tässä nimenomaisessa tapauksessa.

Pitempi vastaus:
- jos käytetään TypeScriptiä / Flowtypea, niin silloin ehdottomasti sulut, koska `const Form = props: Props => {}` ei ole kummassakaan validi syntaksi [1]
- jos käytetään code formatteria, (Prettier, ESLint, whatever), niin tähän voidaan (ja kannattaa) ottaa erikseen kantaa [2]
- sekä kaikki muut jo yllä esille tulleet pointit (versionhallintamuutokset, destrukturointi jne)





[1]: TS: TypeScript Playground
Flow: Try Flow | Flow

[2]:
Prettier: Options · Prettier
ESLint: arrow-parens - Rules
 
Juuh elikkäs MS SQL kyssäri vaihteeksi.

Teen tietojen kopioinnin INSERT INTO lausekkeella.
Laitan uudet avaimet talteen output komennolla.

Pitäisi laittaa talteen myös kopioidun rivin avaintieto eli parina pitää nämä uudet ja vanhat avaimet.

Mitenkäs tuo tehdään?
Koodi:
declare @Avaintable (uusi int, vanha int)
insert into uusitaulu (sarake1, sarake2, sarake3)
output insterted.avain into @Avaintable(uusi)
select tieto1, tieto2, tieto3
from vanhataulu
eli vanhan taulun pääavain pitäisi kohdistaa uuden taulun pääavaimeen.
Taustalla on että pääavaimella kohdistetaan toisessa taulussa tietoja, ja nämäkin tiedot pitää kopioida vanhoilta avaimilta uusille avaimille. Nyt tuo onnistuu avain kerrallaan määrittelemällä alussa kopioitava avain muuttujaan mutta kun näitä kopioitavia avaimia saataa olla useita kymmeniä.
 
Juuh elikkäs MS SQL kyssäri vaihteeksi.

Teen tietojen kopioinnin INSERT INTO lausekkeella.
Laitan uudet avaimet talteen output komennolla.

Pitäisi laittaa talteen myös kopioidun rivin avaintieto eli parina pitää nämä uudet ja vanhat avaimet.

Mitenkäs tuo tehdään?
Koodi:
declare @Avaintable (uusi int, vanha int)
insert into uusitaulu (sarake1, sarake2, sarake3)
output insterted.avain into @Avaintable(uusi)
select tieto1, tieto2, tieto3
from vanhataulu
eli vanhan taulun pääavain pitäisi kohdistaa uuden taulun pääavaimeen.
Taustalla on että pääavaimella kohdistetaan toisessa taulussa tietoja, ja nämäkin tiedot pitää kopioida vanhoilta avaimilta uusille avaimille. Nyt tuo onnistuu avain kerrallaan määrittelemällä alussa kopioitava avain muuttujaan mutta kun näitä kopioitavia avaimia saataa olla useita kymmeniä.
Juuh elikkäs MS SQL kyssäri vaihteeksi.

Teen tietojen kopioinnin INSERT INTO lausekkeella.
Laitan uudet avaimet talteen output komennolla.

Pitäisi laittaa talteen myös kopioidun rivin avaintieto eli parina pitää nämä uudet ja vanhat avaimet.

Mitenkäs tuo tehdään?
Koodi:
declare @Avaintable (uusi int, vanha int)
insert into uusitaulu (sarake1, sarake2, sarake3)
output insterted.avain into @Avaintable(uusi)
select tieto1, tieto2, tieto3
from vanhataulu
eli vanhan taulun pääavain pitäisi kohdistaa uuden taulun pääavaimeen.
Taustalla on että pääavaimella kohdistetaan toisessa taulussa tietoja, ja nämäkin tiedot pitää kopioida vanhoilta avaimilta uusille avaimille. Nyt tuo onnistuu avain kerrallaan määrittelemällä alussa kopioitava avain muuttujaan mutta kun näitä kopioitavia avaimia saataa olla useita kymmeniä.

Helpommin ja nopeimmin menee kun teet uuteen tauluun kentän sille vanhalle avaimelle.
Eli uusi taulu tyyliin

create tabke uusi (
avain identity,
vanha_avain,
kenttä1,
kenttä2,
jne
)

ja sen jälkeen insert into uusi select avain, kenttä1, kenttä2, jne from vanha;

sitten kun loputkin kohdistukset/muunnokset on tehty niin poistat uudesta taulusta vanha_avain- kentän.
 
Helpommin ja nopeimmin menee kun teet uuteen tauluun kentän sille vanhalle avaimelle.
Eli uusi taulu tyyliin

create tabke uusi (
avain identity,
vanha_avain,
kenttä1,
kenttä2,
jne
)

ja sen jälkeen insert into uusi select avain, kenttä1, kenttä2, jne from vanha;

sitten kun loputkin kohdistukset/muunnokset on tehty niin poistat uudesta taulusta vanha_avain- kentän.
Itse taulun sarakkeisiin en kajoa koska tuotantojärjestelmä on kyseessä. Google kuitenkin tuotti tulosta ja sain hoidettua homman kursorilla.
Pseudona suunnilleen näin:
Koodi:
declare @Avaintable (vanha int)
declare @HaettuAvain int
declare kursori cursor fast_forward read_only for select vanha from @Avaintable
insert into @Avaintable
select
avain
from
taulu

open kursori
fetch next from kursori into @HaettuAvain
while @@FETCH_STATUS = 0
begin

... kopioi tiedot sekä siihen liittyvän toisen taulun tiedot ...

fetch next from kursori into @HaettuAvain
end
close kursori
deallocate kursori
 
Itse taulun sarakkeisiin en kajoa koska tuotantojärjestelmä on kyseessä. Google kuitenkin tuotti tulosta ja sain hoidettua homman kursorilla.
Pseudona suunnilleen näin:
Koodi:
declare @Avaintable (vanha int)
declare @HaettuAvain int
declare kursori cursor fast_forward read_only for select vanha from @Avaintable
insert into @Avaintable
select
avain
from
taulu

open kursori
fetch next from kursori into @HaettuAvain
while @@FETCH_STATUS = 0
begin

... kopioi tiedot sekä siihen liittyvän toisen taulun tiedot ...

fetch next from kursori into @HaettuAvain
end
close kursori
deallocate kursori

Voihan sen noinkin tehdä. Tosin rivimäärästä riippuen tuo kursori ja rivi kerrallaan voi olla vähän hidas.

Jos rakenteisiin ei saa koskea ja rivejä on paljon niin tekisin edelleen alkuperäisellä tavalla mutta tulokset ensin temp- tauluun ja sieltä viralliseen uuteen identity_insert päällä jolloin riveille ei enää luoda uusia avaimia syötettäessä viralliseen. Lopuksi temp- taulun poisto.

Harvoin on tullut vastaan niin tiukkoja järjestelmiä ettei datamigraation aikana ole saanut tehdä uusia rakenteita kantaan
 
Voihan sen noinkin tehdä. Tosin rivimäärästä riippuen tuo kursori ja rivi kerrallaan voi olla vähän hidas.

Jos rakenteisiin ei saa koskea ja rivejä on paljon niin tekisin edelleen alkuperäisellä tavalla mutta tulokset ensin temp- tauluun ja sieltä viralliseen uuteen identity_insert päällä jolloin riveille ei enää luoda uusia avaimia syötettäessä viralliseen. Lopuksi temp- taulun poisto.

Harvoin on tullut vastaan niin tiukkoja järjestelmiä ettei datamigraation aikana ole saanut tehdä uusia rakenteita kantaan
No tässä ei ole kyseessä kuin maksimissaan muutama sata riviä niin ei suorituskyvyllä ole kovin isoa merkitystä. Ihan oppimismielessä tämän tein kun SQLlää en ole opetellut ja tässä työssä sitä pääsee näpylöimään. Tämä tietokanta on toimittajan tietokanta ja minä vain lähinnä muuttelen tietoja siellä. Tietokannan ylläpitovastuu on toimittajalla.
Mitä tässä parin vuoden aikana olen opiskellut samalla SQL kieltä niin onhan tuo aivan älyttömän monipuolinen kieli.
 
No tässä ei ole kyseessä kuin maksimissaan muutama sata riviä niin ei suorituskyvyllä ole kovin isoa merkitystä. Ihan oppimismielessä tämän tein kun SQLlää en ole opetellut ja tässä työssä sitä pääsee näpylöimään. Tämä tietokanta on toimittajan tietokanta ja minä vain lähinnä muuttelen tietoja siellä. Tietokannan ylläpitovastuu on toimittajalla.
Mitä tässä parin vuoden aikana olen opiskellut samalla SQL kieltä niin onhan tuo aivan älyttömän monipuolinen kieli.

OT: Eräs vanha pomoni sanoi, että tulee lyömään minua, jos laitan vielä tuotantokantaan kursoreita. En sitten laittanut, niin en tiedä, olisiko hakkaamisia tapahtunut..

Niiden suorituskyky on oikeassa käytössä karmea, luettavuus huono ja ne voi aina korvata paremmalla ratkaisulla. Esim. tietokannan käyminen läpi kaksi kertaa on parempi ratkaisu. Siis vaikka niin, että tehdään tuo temppitaulu ensin ja sen avulla hoidetaan homma toisella iteraatiolla.
 
OT: Eräs vanha pomoni sanoi, että tulee lyömään minua, jos laitan vielä tuotantokantaan kursoreita. En sitten laittanut, niin en tiedä, olisiko hakkaamisia tapahtunut..

Niiden suorituskyky on oikeassa käytössä karmea, luettavuus huono ja ne voi aina korvata paremmalla ratkaisulla. Esim. tietokannan käyminen läpi kaksi kertaa on parempi ratkaisu. Siis vaikka niin, että tehdään tuo temppitaulu ensin ja sen avulla hoidetaan homma toisella iteraatiolla.
No tämä kursori ei ehdi kauaa olla olemassa niin jos selviäisin ilman huutoa :D Tuo nyt oli ensimmäinen toteutus minkä sain toimimaan ilman tietokannan rakenteeseen kajoamista. SQL osaamiseni ei ole vielä kovin kaksista.
Plus tässä kannassa on indeksointikin aika vaihtelevan tasoista ja dataa ei ole siivottu muutamaan vuoteen joten ehkä tämä nyt ei ole relevantti suorituskyvyn kannalta.
Mutta kiitos vinkistä, vältän kursoreita vähänkään isommissa transaktioissa.
 
Niin muuten, miten tuo sama tehdään käytännössä while loopilla? Jostain syystä en löytänyt selkeää esimerkkiä? Eli tallennan vaikka 5 tietuetta väliaikaiseen tauluun, miten käyn ne läpi while loopissa?
Koodi:
declare @Avaintable (vanha int)
declare @HaettuAvain int
declare @Rivimaara
declare
insert into @Avaintable
select
avain
from
taulu
set @Rivimaara= (select count(*) from @Avaintable)
begin
while rivi < @Rivimaara
set @Haettuavain = @Avaintable(vanha) rivi?

... kopioi tiedot sekä siihen liittyvän toisen taulun tiedot ...

rivi = rivi +1
end
 
Niin muuten, miten tuo sama tehdään käytännössä while loopilla? Jostain syystä en löytänyt selkeää esimerkkiä? Eli tallennan vaikka 5 tietuetta väliaikaiseen tauluun, miten käyn ne läpi while loopissa?
Koodi:
declare @Avaintable (vanha int)
declare @HaettuAvain int
declare @Rivimaara
declare
insert into @Avaintable
select
avain
from
taulu
set @Rivimaara= (select count(*) from @Avaintable)
begin
while rivi < @Rivimaara
set @Haettuavain = @Avaintable(vanha) rivi?

... kopioi tiedot sekä siihen liittyvän toisen taulun tiedot ...

rivi = rivi +1
end

Miksi haluat välttämättä käyttää looppia, se ei ole oikea tapa tehdä asioita tietokannassa.

Kannattaa ajatella joukko- opillisesti ja käsitellä datasettejä eikä rivejä. Ainoa poikkeus on jos jotain pitää järjestää ja antaa järjestysnumero mutta silloinkin joku ikkunointi tai order by + sekvenssi toimii paremmin.

Ja kun kerran olet opettelemassa niin opettele kerralla oikea tapa. On joitakin harvoja tilanteita joissa suorat kyselyt eivät toimi ja on pakko käyttää kursoria/looppia/tms mutta älä aloita niistä.
Kun opettelee heti oikean tavan ei ole väliä onko rivejä 100, 1M vai 100M. Silloin ei aina tarvitse keksiä uutta tapaa jos vanha ei toimikaan.

Ja saat siis yhden huudon heti koska ehdotit looppia :D


Oletetaan että on jotenkin tehty taulu jossa on tarvittavat avaimet (avaintaulu). Sen perusteella haluat kopioida jotain sarakkeita tauluista 1 ja 2 tauluun kohde.

Menee vaikka näin:

insert into kohde
select
t1.avain,
t1.kenttä1,
t2.toinen,
t2.kolmas
from
t1, t2, avaintaulu
where
t1.jokukenttä = t2.toinenkenttä
and t1.avain = avaintaulu.avain

Ja vastaavia tehdään niin monta kuin avaintaulun perusteella pitää täyttää uusia tauluja joillakin tiedoilla. Tässä ei ole väliä montako riviä käsitellään. Eikä järjestyksen pitäisi olla edes merkittävää koska oletuksena kannat käsittelevät settejä. Jos kohteeseen halutaan järjestys niin order by perään.

Jos mahtuu niin aina voi tehdä temp- taulun muuttujana, aitona tauluna samaan kantaan jossa toimii, viereiseen kantaan johon on kirjoitusoikeus tai vaikka tekee uuden kannan sitä varten että saa datan konvertoitua jos viralliseen kantaan ei saa tehdä tauluja.

PS. jos näyttää siltä että rivit tulevat selectillä (avain) järjestykseen ilman order by:ta niin se johtuu yleensä siitä että kysely käyttää pääavaimen indeksiä joka on oletuksena järjestetty... toimii siis tuurilla.
 
Mulla pyörii Herokussa Node/React-sovellus. Sovelluksen pyörittämiseen liittyy pari päivittäin ajettavaa node-skriptiä. Saan ajastettua skriptien ajamisen Heroku Scheduler -addonilla, mutta ongelma on se, että en näe skriptien ajosta mitään ajon aloituksen ja loppumisen lisäksi mitään logeja. Mulla on molemmista skripteissä paljon console.logeja / console.erroreita ja oletin, että ne tulostuisi Herokun logeihin.

Näin ei kuitenkaan ole. Skriptit eivät ole osa sovellusta, joten niiden logit eivät tulostu. Herokun logiin tulostuu ainoastaan sovelluksen sisäiset logit. Onko tähän olemassa mitään helppoa ratkaisua? Logitiedot ovat tärkeitä sovelluksen pyörimisen kannalta.
 
Mulla pyörii Herokussa Node/React-sovellus. Sovelluksen pyörittämiseen liittyy pari päivittäin ajettavaa node-skriptiä. Saan ajastettua skriptien ajamisen Heroku Scheduler -addonilla, mutta ongelma on se, että en näe skriptien ajosta mitään ajon aloituksen ja loppumisen lisäksi mitään logeja. Mulla on molemmista skripteissä paljon console.logeja / console.erroreita ja oletin, että ne tulostuisi Herokun logeihin.

Näin ei kuitenkaan ole. Skriptit eivät ole osa sovellusta, joten niiden logit eivät tulostu. Herokun logiin tulostuu ainoastaan sovelluksen sisäiset logit. Onko tähän olemassa mitään helppoa ratkaisua? Logitiedot ovat tärkeitä sovelluksen pyörimisen kannalta.

Tsekkasitko ohjeet: Heroku Scheduler | Heroku Dev Center
 

Statistiikka

Viestiketjuista
261 653
Viestejä
4 537 986
Jäsenet
74 857
Uusin jäsen
FinB

Hinta.fi

Back
Ylös Bottom