Отримайте декілька стовпців із вибраного підзапиту


24
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)

Як ви бачите, я повторюю той самий підзапит, щоб вийти ще один стовпець. Мені цікаво, чи є кращий спосіб зробити це?

id - це первинний ключ в обох таблицях. Я не маю жодних проблем зробити product_special.priority унікальним, якщо це може допомогти.

Відповіді:


11

Припустимо, що комбінація product_special.id, product_special.priorityунікальна

 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)

5

якщо ви не збираєтесь повернути поля як special_price.price та date.date, чому б не псевдоніми імен у підзапиті? напр

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

Чи має ваша мова запитів функцію ПЕРШИЙ () агрегат? Не впевнений, чи зможете ви зробити ПК від product_special складеним між id та пріоритетом (обидва сортування ASC) та змінити пункт ORDER наGROUP BY id, psi.priority

ви МОЖЕ мати змогу повністю видалити пункт ORDER BY і використати його HAVING MIN(psi.priority)


2

Зауважте, що механізм "застосувати крос" із SQL Server вирішив би це, але він недоступний у PostgreSQL. В основному, це було їх рішенням про те, як передавати параметри (які, як правило, посилаються на стовпці, зовнішні за поточним виразом таблиці), до функцій, названих виразами таблиці в пункті FROM Але це виявилося корисним у будь-яких ситуаціях, коли ви хочете уникнути іншого рівня підрядного введення або переміщення речей з пункту FROM до пункту SELECT. PostgreSQL дозволив це зробити, створивши виняток - ви можете передавати такі параметри, якщо вираз є простим викликом функції, але не строго кажучи вбудованим SELECT. Так

left join highestPriorityProductSpecial(p.id) on true

добре, але ні

left join (select * from product_special ps where ps.id = p.id order by priority desc limit 1) on true

незважаючи на те, що саме визначення функції є саме таким.

Отже, це насправді зручне рішення (як мінімум у 9.1): зробіть функцію для вилучення рядка з найвищим пріоритетом, виконуючи межу всередині функції.

Але функції мають той недолік, що план запитів не показуватиме, що відбувається всередині них, і я вважаю, що він завжди вибере вкладений цикл приєднання, навіть коли це може бути не найкращим.


6
cross apply це доступно в Postgres , починаючи з 9.3 (випущена в 2013 році) , але вони вирішили дотримуватися стандарту SQL і використовувати стандартний lateralоператор. У своєму другому запиті замініть left joinнаleft join lateral
a_horse_with_no_name

2

Спробуйте виконати таку команду SQL:

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

1
Ви можете додати більше інформації у відповідь
Ахмад Абухасна

Код, про який йде мова, використовує LIMITі не позначається СУБД (тому це може бути MySQL або Postgres або SQLite або, можливо, деякі інші dbms). Код у відповіді використовує, OUTER APPLYі TOPтому він буде працювати лише в SQL Server (і Sybase), яких немає LIMIT.
ypercubeᵀᴹ

Цей застосовний для сервера sql лише для інших баз даних, ми можемо використовувати внутрішній запит у операторі select.
SANTOSH APPANA

У Postgres немає OUTER APPLY, але є LATERAL , що має бути рівнозначним. Приклад його використання: stackoverflow.com/a/47926042/4850646
Лукас Basquerotto

2

Натхненний відповіддю dezso /dba//a/222471/127433 Я вирішую проблему в PostgreSQL за допомогою масивів, як це:

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)

Правда, це ще лише один стовпець, але в моєму коді я можу легко отримати доступ до двох значень. Сподіваюся, що це працює і для вас.


1

Я просто хочу поставити це тут в крайньому випадку, для всіх, хто використовує двигун бази даних, який не підтримує одну чи більше інших відповідей ...

Ви можете використовувати щось на кшталт:

SELECT (col1 || col2) as col3 

(За допомогою роздільника або форматування col1 та col2 до певної довжини.) А пізніше намалюйте свої дані за допомогою підрядків.

Сподіваюся, хтось вважає це корисним.


0

У DB2 для z / OS використовуйте packта unpackфункції, щоб повернути кілька стовпців у підселектор.

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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.