Limite de se joindre à une seule ligne
-
11-12-2019 - |
Question
J'ai la requête suivante:
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;
Le problème c'est que la table "EsnsSalesOrderItems"
peut avoir le même EsnId
dans les différentes entrées.Je veux restreindre la requête de tirer seulement la dernière entrée dans "EsnsSalesOrderItems"
qui a le même "EsnId"
.
Par "dernier" entrée, je veux dire les éléments suivants:
Celui qui apparaît en dernier dans la table "EsnsSalesOrderItems"
.Ainsi, par exemple si "EsnsSalesOrderItems"
a deux entrées avec "EsnId" = 6
et "createdAt" = '2012-06-19'
et '2012-07-19'
respectivement, il ne devrait me donner de l'entrée de '2012-07-19'
.
La solution
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;
Votre requête dans la question illégale d'un ensemble plus un autre agrégat:
sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount
Simplifié et converti juridique de la syntaxe:
(count(*) * sum(s."price")) AS amount
Mais voulez-vous vraiment à se multiplier avec le comte par groupe?
- Je récupérer la ligne unique par groupe dans "EsnsSalesOrderItems"
avec DISTINCT ON
.Explication détaillée:
J'ai également ajouté des alias de table et mise en forme pour faire la requête plus facile à analyser pour les yeux humains.Si vous pouviez éviter de chameau cas, vous pourriez se débarrasser de tous les guillemets obscurcissement de la vue.
Autres conseils
Quelque chose comme:
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
cela permet de sélectionner la dernière "EsnId"
à partir de "EsnsSalesOrderItems"
basé sur la colonne creation_date
.Comme vous n'avez pas de poste de la structure de vos tables, j'ai dû "inventer" un nom de colonne.Vous pouvez utiliser n'importe quelle colonne vous permet de définir un ordre sur les lignes qui vous convient.
Mais rappelez-vous le concept de la "dernière ligne" n'est valable que si vous spécifier une commande ou de l'lignes.Un tableau en tant que tel n'est pas ordonnée, ni le résultat d'une requête à moins que vous spécifiez un order by
Necromancing parce que les réponses sont obsolètes.
Profitez de l' LATERAL
mot-clé introduit dans PG 9.3
gauche | droite | inner JOIN LATÉRALE
Je vais vous expliquer avec un exemple:
En supposant que vous avez une table "Contacts".
Maintenant, les contacts des unités organisationnelles.
Ils peuvent avoir une unité à un point dans le temps, mais N OUs N points dans le temps.
Maintenant, si vous avez une requête à des contacts et OU dans une période de temps (pas une date de rapport, mais une plage de dates), vous pourriez N multiplié par le nombre d'enregistrement si vous venez de faire un left join.
Ainsi, pour afficher l'unité d'organisation, vous avez juste besoin de se joindre à la première unité d'organisation pour chaque contact (où ce sera la première est un critère arbitraire - lors de la prise de la dernière valeur, par exemple, qui est juste une autre façon de dire que la première valeur lorsque triés par ordre décroissant d'ordre de date).
Dans SQL server, vous pouvez utiliser de la croix-appliquer (ou plutôt de l'EXTÉRIEUR, car nous avons besoin d'une jointure gauche), qui va appeler une fonction table sur chaque ligne il a de rejoindre.
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
Dans PostgreSQL, a partir de la version 9.3, vous pouvez aussi le faire - il suffit d'utiliser la LATERAL
mot-clé pour obtenir le même:
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
Essayez d'utiliser une sous-requête dans votre clause.Un résumé exemple:
SELECT
*
FROM table1
JOIN table2 ON table2.id = (
SELECT id FROM table2 WHERE table2.table1_id = table1.id LIMIT 1
)
WHERE
...