Limite unisciti a una riga
-
11-12-2019 - |
Domanda
Ho la seguente query:
SELECT sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount, 'rma' as
"creditType", "Clients"."company" as "client", "Clients".id as "ClientId", "Rmas".*
FROM "Rmas" JOIN "EsnsRmas" on("EsnsRmas"."RmaId" = "Rmas"."id")
JOIN "Esns" on ("Esns".id = "EsnsRmas"."EsnId")
JOIN "EsnsSalesOrderItems" on("EsnsSalesOrderItems"."EsnId" = "Esns"."id" )
JOIN "SalesOrderItems" on("SalesOrderItems"."id" = "EsnsSalesOrderItems"."SalesOrderItemId")
JOIN "Clients" on("Clients"."id" = "Rmas"."ClientId" )
WHERE "Rmas"."credited"=false AND "Rmas"."verifyStatus" IS NOT null
GROUP BY "Clients".id, "Rmas".id;
.
Il problema è che la tabella "EsnsSalesOrderItems"
può avere lo stesso EsnId
in diverse voci.Voglio limitare la query per estrarre solo l'ultima voce in "EsnsSalesOrderItems"
che ha lo stesso "EsnId"
.
Per "ultima" voce intendo quanto segue:
Quello che appare per ultimo nella tabella "EsnsSalesOrderItems"
.Quindi, ad esempio, se "EsnsSalesOrderItems"
ha due voci con "EsnId" = 6
e "createdAt" = '2012-06-19'
e '2012-07-19'
rispettivamente, dovrebbe darmi solo la voce da '2012-07-19'
.
Soluzione
SELECT (count(*) * sum(s."price")) AS amount
, 'rma' AS "creditType"
, c."company" AS "client"
, c.id AS "ClientId"
, r.*
FROM "Rmas" r
JOIN "EsnsRmas" er ON er."RmaId" = r."id"
JOIN "Esns" e ON e.id = er."EsnId"
JOIN (
SELECT DISTINCT ON ("EsnId") *
FROM "EsnsSalesOrderItems"
ORDER BY "EsnId", "createdAt" DESC
) es ON es."EsnId" = e."id"
JOIN "SalesOrderItems" s ON s."id" = es."SalesOrderItemId"
JOIN "Clients" c ON c."id" = r."ClientId"
WHERE r."credited" = FALSE
AND r."verifyStatus" IS NOT NULL
GROUP BY c.id, r.id;
.
La tua query nella domanda ha un aggregato illegale su un altro aggregato:
sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount
.
Semplificato e convertito in sintassi legale:
(count(*) * sum(s."price")) AS amount
.
Ma vuoi davvero moltiplicarsi con il conteggio per gruppo?
I Recupera la singola riga per gruppo in "EsnsSalesOrderItems"
con DISTINCT ON
.Spiegazione dettagliata:
- .
- Seleziona la prima riga in ciascun gruppo per gruppo?
Ho anche aggiunto alias e formattazione di tabelle per rendere la query più facile da analizzare per gli occhi umani.Se è possibile Evita il caso di cammello che potrestiSbarazzarsi di tutte le virgolette doppie che soffocano la vista.
Altri suggerimenti
Qualcosa come:
join (
select "EsnId",
row_number() over (partition by "EsnId" order by "createdAt" desc) as rn
from "EsnsSalesOrderItems"
) t ON t."EsnId" = "Esns"."id" and rn = 1
.
Selezionerà l'ultima "EsnId"
in base a "EsnsSalesOrderItems"
tagCode basata sulla colonna creation_date
. Come non hai pubblicato la struttura delle tabelle, ho dovuto" inventare "un nome di colonna. Puoi usare qualsiasi colonna che ti permette di definireUn ordine sulle righe che ti si addice.
Ma ricorda che il concetto di "ultima riga" è valido solo se si specifica un ordine o le righe.Una tabella come tale non viene ordinata, né è il risultato di una query a meno che si specifica un order by
Necromancing perché le risposte sono obsolete.
.
Approfitta della parola chiave LATERAL
introdotta in PG 9.3
Sinistra | Destra |. Iscriviti interni Laterale
Spiegherò con un esempio:
Supponendo che tu abbia un tavolo "contatti".
.
Ora i contatti hanno unità organizzative.
Possono avere uno ou in un punto nel tempo, ma n ous a n punti nel tempo.
Ora, se devi interrogare contatti e in un periodo di tempo (non una data di riferimento, ma un intervallo di date), è possibile che n-fold aumenta il conteggio dei record se hai appena fatto un Iscriviti a sinistra.
.
Quindi, per visualizzare l'OU, è necessario solo unirti al primo ou per ogni contatto (dove quale sarà il primo è un criterio arbitrario - quando si prende l'ultimo valore, ad esempio, questo è solo un altro modo per dire il primo valore quando ordinato Discendente l'ordine della data).
In SQL-Server, si userebbe a cross-application (o uter esterno si applica perché abbiamo bisogno di un join sinistro), che invocherà una funzione di valore a tavola su ogni riga che deve aderire.
.
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
-- CROSS APPLY -- = INNER JOIN
OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(@in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(@in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE
in PostgreSQL, A partire dalla versione 9.3, Puoi farlo, utilizzare anche la parola chiave LATERAL
per ottenere lo stesso:
.SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
Prova a utilizzare una sottoquery nella tua clausola sulla clausola.Un esempio astratto:
SELECT
*
FROM table1
JOIN table2 ON table2.id = (
SELECT id FROM table2 WHERE table2.table1_id = table1.id LIMIT 1
)
WHERE
...
.