Autor Téma: Do subselect-u dosadiť aktuálnu hodnotu poľa z hlavného select-u  (Přečteno 938 krát)

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Kód: [Vybrat]
SELECT DISTINCT
  METERS.IDMETERS,

  (SELECT FIRST(1) CONSUMPTIONS.AMOUNT
FROM METERS
  LEFT OUTER JOIN CONSUMPTIONS ON (METERS.IDMETERS = CONSUMPTIONS.FKMETERS)
WHERE
  CONSUMPTIONS.FKMETERS = 2170  // Aktuálny METERS.IDMETERS
ORDER BY
  CONSUMPTIONS.READING DESC) AS PRIORAMOUNT,

  (SELECT FIRST(1) CONSUMPTIONS.AMOUNT
FROM  METERS
  LEFT OUTER JOIN CONSUMPTIONS ON (METERS.IDMETERS = CONSUMPTIONS.FKMETERS)
WHERE
  CONSUMPTIONS.FKMETERS = 2170 // Aktuálny METERS.IDMETERS
ORDER BY
  CONSUMPTIONS.READING ASC) AS NEXTAMOUNT

FROM ...
V jednom riadku mám mať hodnotu a v druhom NULL. Mám rovnakú hodnotu v oboch riadkoch. Robím to na strane klienta cez TFDQuery.
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Můžeš to napsat srozumitelněji? Vůbec netuším, co chceš udělat. Titulek mluví o dosazování údajů z vnějšího selectu do vnitřního selectu, SQL je totálně nečitelné (aspoň ho vhodně odsaď!), popisek dole skoro vypadá, jako kdybys hledal ORDER BY fieldname NULLS FIRST.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Kód: Delphi [Vybrat]
  1. SELECT DISTINCT METERS.IDMETERS,
  2.  
  3.   (SELECT FIRST(1) CONSUMPTIONS.AMOUNT
  4.   FROM METERS
  5.     LEFT OUTER JOIN CONSUMPTIONS ON (METERS.IDMETERS = CONSUMPTIONS.FKMETERS)
  6.   WHERE CONSUMPTIONS.FKMETERS = METERS.IDMETERS) AS PRIORAMOUNT,
  7. ...
  8.  
Hm, ten popis patrí k výsledku. Má tam byť napr.
              IDMeters | PRIORAMOUNT
1. riadok     1                   50,25               
2. riadok     2                    null                 

a ja dostávam

1. riadok     1                   50,25               
2. riadok     2                   50,25               

V oboch vrátených riadkoch výsledku je METERS.IDMETERS (z vonkajšieho select-u) vrátená v subselectoch rovnaká hodnota. A to posledná získaná.
« Poslední změna: 14-02-2017, 19:36:46 od Stanislav Hruška »
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Dávam slovný popis problému, úlohy.
Vyberám hodnoty pre určitý časový úsek určený podmienkou BETWEEN FIRSTDATE AND LASTDATE. K tomu potrebujem doplniť prvú hodnotu pred FIRSTDATE a prvú hodnotu za LASTDATE.

Jedná sa zadávanie stavov vodomerov za rok. A tie dva údaje mi majú slúžiť na kontrolu správnosti práve zadávaných údajov.
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Ešte tabuľka:

CREATE TABLE CONSUMPTIONS (
  IDCONSUPMTIONS PRIMARYKEY DEFAULT '0' NOT NULL,
  FKFOCS FOREIGNKEY NOT NULL,
  FKLANDLORD_FLAT FOREIGNKEY NOT NULL,
  FKMETERS FOREIGNKEY NOT NULL,
  READING DUEDATE NOT NULL,
  AMOUNT CURRENCY DEFAULT '0.0' NOT NULL,
  CORRECTION CURRENCY DEFAULT '0.0' NOT NULL,
  NOTE MEMO,
  KINDSTATE SMALLINTVAL DEFAULT '0' NOT NULL);

ALTER TABLE CONSUMPTIONS ADD CONSTRAINT PK_CONSUMPTIONS PRIMARY KEY (IDCONSUPMTIONS);
ALTER TABLE CONSUMPTIONS ADD CONSTRAINT FK_CONSUMPTIONS_FOCS FOREIGN KEY (FKFOCS) REFERENCES FOCS(IDFOCS);
ALTER TABLE CONSUMPTIONS ADD CONSTRAINT FK_CONSUMPTIONS_LANDLORD_FLAT FOREIGN KEY (FKLANDLORD_FLAT) REFERENCES LANDLORD_FLAT(IDLANDLORD_FLAT);
ALTER TABLE CONSUMPTIONS ADD CONSTRAINT FK_CONSUMPTIONS_METERS FOREIGN KEY (FKMETERS) REFERENCES METERS(IDMETERS) ON DELETE CASCADE;
COMMENT ON COLUMN CONSUMPTIONS.KINDSTATE IS '0 Reading, 1 Initial, 2 Final';

GRANT SELECT, INSERT, DELETE, REFERENCES, UPDATE ON CONSUMPTIONS TO SYSDBA WITH GRANT OPTION;
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Kód: Delphi [Vybrat]
  1. SELECT DISTINCT METERS.IDMETERS,
  2.  
  3.   (SELECT FIRST(1) CONSUMPTIONS.AMOUNT
  4.   FROM METERS
  5.     LEFT OUTER JOIN CONSUMPTIONS ON (METERS.IDMETERS = CONSUMPTIONS.FKMETERS)
  6.   WHERE CONSUMPTIONS.FKMETERS = METERS.IDMETERS) AS PRIORAMOUNT,
  7. ...
  8.  
Byl bych tedy spokojenější, kdybys to naformátoval takhle, ale aspoň už je srozumitelné, co děláš.
Kód: SQL [Vybrat]
  1. SELECT DISTINCT METERS.IDMETERS,
  2.   (
  3.     SELECT FIRST 1 CONSUMPTIONS.AMOUNT
  4.     FROM METERS
  5.     LEFT OUTER JOIN CONSUMPTIONS ON CONSUMPTIONS.FKMETERS=METERS.IDMETERS
  6.     WHERE CONSUMPTIONS.FKMETERS=METERS.IDMETERS
  7.   ) AS PRIORAMOUNT,
  8.   ...
  9. FROM METERS
  10. ...

Problém 1 (ten menší): Proč tam máš OUTER JOIN? Tohle je úloha na normální JOIN. Respektive, tohle je úloha na vůbec žádný JOIN.

Problém 2 (ten větší): FIRST nemá smysl bez ORDER BY.

Takže to přepiš:
Kód: SQL [Vybrat]
  1. SELECT METERS.IDMETERS,
  2.   (
  3.     SELECT FIRST 1 CONSUMPTIONS.AMOUNT
  4.     FROM CONSUMPTIONS
  5.     WHERE CONSUMPTIONS.FKMETERS=METERS.IDMETERS
  6.     ORDER BY CONSUMPTIONS.SOMEFIELD
  7.   ) AS PRIORAMOUNT,
  8.   ...
  9. FROM METERS
  10. ...

Mimochodem, s využitím CTE (common table expression) by ses mohl úplně zbavit toho subselectu, což by bylo velmi vhodné, kdybys z něj chtěl zjistit víc údajů (a i v jiných případech). Nevím přesně, co děláš, a nechce se mi tvoje kódy zkoumat, ale zhruba by to vypadalo tak, že si přes CTE nadefinuješ dotaz, který ti určí identifikátory do tabulky CONSUMPTIONS, a ty následně použiješ pro normální LEFT JOIN. Něco jako:
Kód: SQL [Vybrat]
  1. WITH CONSUMPTIONCHOICE AS (
  2.   SELECT FKMETERS, MAX(DATEOFEVENT) AS DATEOFEVENT
  3.   FROM CONSUMPTIONS
  4.   WHERE DATEOFEVENT<:datum_zacatku_intervalu
  5.   GROUP BY FKMETERS
  6. )
  7. SELECT METERS.IDMETERS, CONSUMPTIONS.AMOUNT
  8. FROM METERS
  9. LEFT JOIN CONSUMPTIONCHOICE
  10.   ON CONSUMPTIONCHOICE.FKMETERS=METERS.IDMETERS
  11. LEFT JOIN CONSUMPTIONS
  12.   ON CONSUMPTIONS.FKMETERS=CONSUMPTIONCHOICE.FKMETERS
  13.      AND CONSUMPTIONS.DATEOFEVENT=CONSUMPTIONCHOICE.DATEOFEVENT
Bude to samozřejmě fungovat jen v případě, že skutečně dokážeš identifikovat záznam v CONSUMPTIONS jednoznačně, ale protože jako rozumný člověk samozřejmě máš v každé tabulce primární klíč typu INTEGER, tak to nebude problém...

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Citace
Problém 1 (ten menší): Proč tam máš OUTER JOIN? Tohle je úloha na normální JOIN. Respektive, tohle je úloha na vůbec žádný JOIN.
Lebo merač nemusí mať ešte žiaden stav - odpočet.
Citace
Problém 2 (ten větší): FIRST nemá smysl bez ORDER BY.
Hneď v prvom príspevku to mám. Len som to pre zjednodušenie dodatočne vyhodil.
Citace
Takže to přepiš:
Ale to je presne to čo mám v prvom príspevku. Keďže Ťa "poznám", tak viem, že si nerobíš srandu. Počítam s prevádzkovou slepotou.  Problém je v riadku
Kód: [Vybrat]
WHERE CONSUMPTIONS.FKMETERS=METERS.IDMETERSkde METERS.IDMETERS má vždy jednu hodnotu - tú poslednú.
Citace
Mimochodem, s využitím CTE
O tom som ešte nepočul. Naštudujem.
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Citace
Takže to přepiš:
Ale to je presne to čo mám v prvom príspevku.
No to právě nemáš. Všimni si, že v mé verzi vůbec není join. V tvé je, a to "shodou okolností" na stejnou tabulku, se kterou pracuješ ve vnějším selectu a jejíž obsah chceš dostat dovnitř. To je ten tvůj problém.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Abys to měl úplně "po lopatě": Jak funguje tenhle dotaz:
Kód: SQL [Vybrat]
  1. SELECT DISTINCT METERS.IDMETERS,
  2.   (
  3.     SELECT FIRST 1 CONSUMPTIONS.AMOUNT
  4.     FROM METERS
  5.     LEFT OUTER JOIN CONSUMPTIONS ON CONSUMPTIONS.FKMETERS=METERS.IDMETERS
  6.     WHERE CONSUMPTIONS.FKMETERS=METERS.IDMETERS
  7.     ORDER BY CONSUMPTIONS.READING DESC
  8.   ) AS PRIORAMOUNT,
  9.   ...
  10. FROM METERS
  11. ...
Prochází se postupně záznamy v tabulce METERS. Z každého záznamu se vezme sloupec IDMETERS a následně se k němu dopočítá další údaj pomocí subselectu, a to takto:

Prochází se postupně záznamy v tabulce METERS. V celé tabulce. Ke každému nalezenému záznamu se přijoinují záznamy z tabulky CONSUMPTIONS, pro které platí shoda těch klíčových polí v sekci "ON" JOINu, eventuálně NULL, pokud žádný takový záznam neexistuje. Podmínkou WHERE následně ty NULL záznamy zase zrušíš (tzn. LEFT OUTER JOIN se mění na FULL JOIN, akorát s horším výkonem). Tento výsledek se seřadí podle kriteria daného v ORDER BY a vezme se z něj první záznam. Jenže samozřejmě to bude pokaždé ten stejný záznam, protože začínáš s celou tabulkou METERS, ne s aktuálně vybraným řádkem z vnějšího SELECTu.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Citace
Problém 1 (ten menší): Proč tam máš OUTER JOIN? Tohle je úloha na normální JOIN. Respektive, tohle je úloha na vůbec žádný JOIN.
Lebo merač nemusí mať ešte žiaden stav - odpočet.
To je sice pravda, ale 1) to ti právě zajistí ten subselect, že když nenajde vyhovující záznam, tak nastaví pole výsledku na NULL, a 2) i kdyby to tak nebylo, tak kombinací LEFT OUTER JOIN tabulka ON field1=field2 WHERE field1=field2 všechny ty NULL výsledky stejně zrušíš.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
Ještě poslední věc, opět týkající se zápisu (s přehlednějším zápisem se o dost lépe hledají chyby, jak hned uvidíš): Kdybys používal aliasy pro tabulky, tak ti hnedka bude zřejmé, že ten tvůj původní dotaz musí být špatně. Zde je přepsaný na aliasy A, B, C:
Kód: SQL [Vybrat]
  1. SELECT DISTINCT A.IDMETERS,
  2.   (
  3.     SELECT FIRST 1 B.AMOUNT
  4.     FROM METERS C
  5.     LEFT OUTER JOIN CONSUMPTIONS B ON B.FKMETERS=C.IDMETERS
  6.     WHERE B.FKMETERS=C.IDMETERS
  7.     ORDER BY B.READING DESC
  8.   ) AS PRIORAMOUNT,
  9.   ...
  10. FROM METERS A
  11. ...
Nic jsem neměnil, jen jsem místo přímých odkazů na tabulky použil aliasy. Tady bys měl hned na první pohled vidět, že subselect nijak neodkazuje na alias A, tzn. pro výpočet jeho výsledku je zcela nepodstatné, který řádek z A zrovna řešíš... Srovnej s mojí verzí, opět přepsanou na aliasy:
Kód: SQL [Vybrat]
  1. SELECT A.IDMETERS,
  2.   (
  3.     SELECT FIRST 1 B.AMOUNT
  4.     FROM CONSUMPTIONS B
  5.     WHERE B.FKMETERS=A.IDMETERS
  6.     ORDER BY B.SOMEFIELD
  7.   ) AS PRIORAMOUNT,
  8.   ...
  9. FROM METERS A
  10. ...
Tady to provázání je evidentní.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Vďaka za námahu a čas. Takže prevádzkovou slepotou trpím ja :( Nechám si to na zajtra. Teraz by som to už nezvládol.
Pozeral som na to CTE. Pekná vec. Tam ale mám jeden problém. V prvej časti potrebujem mať dve podmienky vedľa seba (jeden riadok). Ale ja ich viem dostať jedine pod seba pomocou UNION. Je to vôbec možné? Nič také som nenašiel.
S tým aliasom si to presne vystihol.
« Poslední změna: 14-02-2017, 21:45:47 od Stanislav Hruška »
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 2877
  • Karma: 27
    • Verze Delphi: XE7 professional
Tými podmienkami myslím samostatné SELECT-y
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline pepak

  • Guru
  • *****
  • Příspěvků: 1290
  • Karma: 28
    • Pepak.net
CTE tabulek si můžeš nadefinovat, kolik chceš...

 

S rychlou odpovědí můžete používat BB kódy a emotikony jako v běžném okně pro odpověď, ale daleko rychleji.

Upozornění: do tohoto tématu bylo naposledy přispěno před 120 dny.
Zvažte prosím založení nového tématu.

Jméno: E-mail:
Ověření:
Datový typ v Delphi, který má True a False: