Создание двумерной таблицы результатов в SQL

#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.