SELECT
*,
p.name AS name,
p.image,
p.price,
(
SELECT ps.price
FROM product_special ps
WHERE p.id = ps.id
AND ps.date < NOW()
ORDER BY ps.priority ASC, LIMIT 1
) AS special_price,
(
SELECT ps.date
FROM product_special ps
WHERE p.id = ps.id
AND ps.date < NOW()
ORDER BY ps.priority ASC, LIMIT 1
) AS date
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id)
Comme vous pouvez le voir, je répète la même sous-requête juste pour extraire une autre colonne. Je me demande s'il existe une meilleure façon de procéder?
id est la clé primaire dans les deux tables. Je n'ai aucun problème à rendre product_special.priority unique si cela peut aider.
En supposant une combinaison product_special.id, product_special.priority
est unique
SELECT p.*, special_price,special_date
FROM product p
LEFT JOIN
(
SELECT ps.id, ps.price as special_price, ps.`date` as special_date
FROM product_special ps
INNER JOIN
(
SELECT id, MIN(priority) as min_priority
FROM product_special
GROUP BY id
) ps2
ON (ps2.id = ps.id)
)a ON (a.id=p.id)
sauf si vous avez l'intention de renvoyer les champs en tant que special_price.price et date.date, pourquoi ne pas alias les noms à l'intérieur de la sous-requête? par exemple.
SELECT p.*, p.name AS name, p.image, p.price, ps.*
FROM product p
LEFT JOIN
(SELECT
psi.price as special_price, psi.date as my_date
FROM product_special psi
WHERE
p.id = psi.id AND
psi.date < NOW()
ORDER BY psi.priority ASC, LIMIT 1
) AS ps ON
p.id = ps.id
Votre langage de requête a-t-il une fonction d'agrégation FIRST ()? Je ne sais pas si vous pouvez faire du PK de product_special un composite entre id et priorité (les deux sont de type ASC) et changer la clause ORDER en GROUP BY id, psi.priority
vous POUVEZ être en mesure de supprimer la clause ORDER BY entièrement et d'utiliser HAVING MIN(psi.priority)
Notez que le mécanisme "cross-apply" de SQL Server résoudrait cela, mais il n'est pas disponible dans PostgreSQL. Fondamentalement, c'était leur solution pour savoir comment passer des paramètres (qui ont tendance à être des références à des colonnes externes à l'expression de table actuelle) aux fonctions appelées expressions de table dans la clause FROM. Mais il s'est avéré utile pour toutes sortes de situations où vous souhaitez éviter un autre niveau d'imbrication de sous-requête ou de déplacement de choses de la clause FROM vers la clause SELECT. PostgreSQL a rendu cela possible en faisant une sorte d'exception - vous pouvez passer des paramètres comme ça si l'expression est un simple appel de fonction mais pas à proprement parler un SELECT intégré. Donc
left join highestPriorityProductSpecial(p.id) on true
est ok, mais pas
left join (select * from product_special ps where ps.id = p.id order by priority desc limit 1) on true
même si la définition de la fonction est précisément cela.
Donc, c'est en fait une solution pratique (en 9.1 au moins): créez une fonction pour extraire votre ligne de priorité la plus élevée en faisant la limite à l'intérieur de la fonction.
Mais les fonctions ont l'inconvénient que le plan de requête ne montrera pas ce qui se passe à l'intérieur et je pense qu'il choisira toujours une jointure en boucle imbriquée, même lorsque ce n'est pas le meilleur.
Inspiré par la réponse de dezso https://dba.stackexchange.com/a/222471/1274 Je résous le problème dans PostgreSQL en utilisant des tableaux, comme ceci:
SELECT
*,
p.name AS name,
p.image,
p.price,
(
SELECT ARRAY[ps.price, ps.date]
FROM product_special ps
WHERE p.id = ps.id
AND ps.date < NOW()
ORDER BY ps.priority ASC, LIMIT 1
) AS special_price_and_date
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id)
Certes, ce n'est encore qu'une colonne, mais dans mon code, je peux facilement accéder aux deux valeurs. J'espère que cela fonctionne aussi pour vous.
Essayez la commande SQL suivante:
SELECT p.name,p.image,p.price,pss.price,pss.date
FROM Product p OUTER APPLY(SELECT TOP(1)*
FROM ProductSpecial ps
WHERE p.Id = ps.Id ORDER BY ps.priority )as pss
Je veux juste mettre cela ici en dernier recours, pour tous ceux qui utilisent un moteur de base de données qui ne prend pas en charge une ou plusieurs des autres réponses ...
Vous pouvez utiliser quelque chose comme:
SELECT (col1 || col2) as col3
(Avec séparateur ou formatage col1 et col2 à une longueur spécifique.) Et dessinez ensuite vos données à l'aide de sous-chaînes.
J'espère que quelqu'un le trouvera utile.
Dans DB2 pour z/OS, utilisez les fonctions pack
et unpack
pour renvoyer plusieurs colonnes dans une sous-sélection.
SELECT
*,
p.name AS name,
p.image,
p.price,
unpack((select PACK (CCSID 1028,
ps.price,
ps.date)
FROM product_special ps
WHERE p.id = ps.id
AND ps.date < NOW()
ORDER BY ps.priority ASC, LIMIT 1)) .* AS (SPECIAL_PRICE double, DATE date)
FROM product p LEFT JOIN product_special ps ON (p.id = ps.id);