#sql #sql-server-2008
#sql #sql-server-2008
Вопрос:
У меня есть список товаров, который обновляется ежедневно, где каждый товар попадает в один из четырех ценовых диапазонов.
Следующий запрос выведет количество товаров в каждом ценовом диапазоне по дате, но он помещает каждую комбинацию диапазона / даты в отдельную строку.
SELECT PB.band, Count(L.ID) as Count
FROM ListItem L
join PriceBand PB on L.price >= PB.loprice and L.price <= PB.hiprice
Where ListID = 1 and ranking <= 100 and band >= 1 and band <= 4
Group by date, PB.band
order by date asc, band asc
Набор результатов выглядит следующим образом (для иллюстрации использованы два дня):
date band Count
2010-12-08 1 20
2010-12-08 2 17
2010-12-08 3 32
2010-12-08 4 26
2010-12-09 1 19
2010-12-09 2 16
2010-12-09 3 33
2010-12-09 4 27
Хотя я могу выполнить итерацию через это, чтобы создать таблицу, которая мне действительно нужна, есть ли разумный способ написать запрос для возврата данных в двумерной форме следующим образом:
date band1 band2 band2 band3
2010-12-08 20 17 32 26
2010-12-09 19 16 33 27
Спасибо всем.
Ответ №1:
SELECT [date],
COUNT(CASE WHEN PB.band = 1 THEN L.ID END) AS band1,
COUNT(CASE WHEN PB.band = 2 THEN L.ID END) AS band2,
COUNT(CASE WHEN PB.band = 3 THEN L.ID END) AS band3,
COUNT(CASE WHEN PB.band = 4 THEN L.ID END) AS band4
FROM ListItem L
join PriceBand PB on L.price >= PB.loprice and L.price <= PB.hiprice
Where ListID = 1 and ranking <= 100 and band >= 1 and band <= 4
Group by date
order by date asc
Комментарии:
1. Спасибо, Мартин. идеально на 100%. Я не был знаком с CASE в SQL, но теперь знаком.
Ответ №2:
Проверьте предложение PIVOT в T-SQL. =>Страница MSDN
Я не уверен в точной форме запроса, но он должен выглядеть примерно так (пожалуйста, дайте мне знать, если это сработает):
SELECT [date],[1],[2],[3],[4]
FROM ListItem L
join PriceBand PB on L.price >= PB.loprice and L.price <= PB.hiprice
Where ListID = 1 and ranking <= 100 and band >= 1 and band <= 4
PIVOT(COUNT(L.ID) FOR PB.band IN([1],[2],[3],[4])) as pvt
order by [date] asc
Комментарии:
1. Спасибо, Дмитрий. Это тоже выглядит интересно, но ответ Мартина был прост в реализации и быстро помог мне решить проблему.
2. Что ж, решение Мартина хорошо, когда у вас есть только 4 диапазона. Но также полезно знать PIVOT: вам может понадобиться 10, 20 категорий, и быстрее написать предложение pivot, чем 20 предложений case. Я не знаю о производительности, но я предполагаю, что PIVOT также быстрее. Недостаток — PIVOT amp; UNPIVOT предназначен только для MSSQL >= 2005.
3. Кстати, если ваш ListItem не может принадлежать нескольким группам (или ни к одной) Я думаю, было бы неплохо удалить hiprice из PriceBand и использовать loprice следующего диапазона в качестве верхней границы. В вашей текущей конфигурации диапазоны могут перекрываться, и между диапазонами могут быть промежутки. (если это не то, что вы хотите, конечно)
Ответ №3:
Во-первых, «дата» — это ужасное название столбца, поэтому я надеюсь, что это просто пример. Во-вторых, поскольку вы не использовали псевдоним с ним, я понятия не имею, в какой таблице он на самом деле, поэтому вот мое лучшее предположение:
SELECT
LI.list_date,
SUM(CASE WHEN PB.band = 1 THEN 1 ELSE 0 END) AS band_1_count,
SUM(CASE WHEN PB.band = 2 THEN 1 ELSE 0 END) AS band_2_count,
SUM(CASE WHEN PB.band = 3 THEN 1 ELSE 0 END) AS band_3_count,
SUM(CASE WHEN PB.band = 4 THEN 1 ELSE 0 END) AS band_4_count
FROM
List_Item LI
INNER JOIN Price_Band PB ON
PB.low_price <= LI.price AND
PR.high_price >= LI.price AND
PB.band <= 4
WHERE
LI.list_id = 1 AND
LI.ranking <= 100 AND
GROUP BY
LI.list_date
ORDER BY
LI.list_date
Комментарии:
1. Согласовано. В этом примере я немного разболтался с синтаксисом. Увеличение для вызова этого и хорошего форматирования. Хотя это был COUNT, а не SUM, но это мелочь.
2. В этом случае будет работать SUM, поскольку я использую 1 или 0 в качестве результата инструкции CASE. Мартин использует L.ID столбец (или NULL, если строка не соответствует критериям), поэтому в этом случае работает COUNT.