Функция подсчета сводок SQL-сервера работает неправильно, если используется с внутренним запросом - PullRequest
0 голосов
/ 29 апреля 2019

Я думаю, что есть ошибка в моей сводной функции SQL-сервера, потому что я не могу найти никакого другого объяснения.

Я использую простую сводную диаграмму и использую точный синтаксис, показанный в MSDN.но сводный ответ показывает одно и то же число для всех столбцов с эквивалентным значением количества всех недель !!!(слева - результат запроса, а справа - то, что я хочу) left is query result and right is what I want[

вот мой запрос:

SELECT
  *
FROM (SELECT
    r.cutomer_id
   ,c.[Week]
   ,r.id
  FROM r
  JOIN c
    ON r.Create_date = c.Date
  WHERE Is_ride = 1
  AND ((Create_date_int BETWEEN 20190302 AND 20190319)
  OR (Create_date_int BETWEEN 20190406 AND 20190426))) p
PIVOT
(
COUNT(id)
FOR [Week] IN
([9], [10], [11], [12], [14], [15], [16], [17])
) AS pvt

вот некоторые тестовые данные, представляющие "p"output (я только что изменил номера идентификаторов, недели - это то же число, которое вы получаете из запроса)

DROP TABLE IF EXISTS #t
CREATE TABLE #t (
  customer_id INT
 ,WEEK INT
 ,id INT
)

INSERT #t (customer_id, WEEK, id)
  VALUES (12032, 10, 8607)
  , (43551, 10, 8721051)
  , (55025, 10, 81200)
  , (198874, 10, 861362)
  , (99675, 10, 867081)
  , (19387, 10, 863656)
  , (12526, 10, 8603706)
  , (19503, 10, 860924)
  , (37597, 10, 860909)
  , (136019, 10, 8610674);

, поэтому я подумал, что с моим запросом что-то не так, но затем изменил запрос на следующий:

SELECT
  r.cutomer_id
 ,c.[Week]
 ,r.id INTO #t
FROM r
JOIN c
  ON r.Create_date = c.Date
WHERE Is_ride = 1
AND ((Create_date_int BETWEEN 20190302 AND 20190319)
OR (Create_date_int BETWEEN 20190406 AND 20190426))

SELECT
  *
FROM #t
PIVOT
(
COUNT(id)
FOR [Week] IN
([9], [10], [11], [12], [14], [15], [16], [17])
) AS pvt

и все работает просто отлично!также, если я удаляю столбец r.id из select и изменяю его на count(week), он работает нормально !!!

также, если я только изменяю where на

   WHERE Is_ride = 1
    AND ((Create_date_int BETWEEN 20190302 AND 20190319)
    OR (Create_date_int BETWEEN 20190406 AND 20190426))
    and passenger_id in (43551,12032,136019)
            ) p

itработает отлично!!!

Может кто-нибудь дать мне объяснение?

1 Ответ

1 голос
/ 29 апреля 2019

Понятия не имею, почему вы получаете результат, показанный на ваших изображениях.Возможно, какая-то информация отсутствует.Вот MVCE с кодом, закомментированным для создания большего набора тестовых данных.

CREATE TABLE r(
    id int identity,
    customer_id int, 
    create_date date, 
    Create_date_int AS CONVERT( int, CONVERT( char(8), create_date, 112)),
    is_ride bit)
INSERT INTO r(customer_id, create_date, is_ride)
SELECT customer_id, '20190307', 1
FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))x(customer_id)
--SELECT TOP 100000
--       ABS(CHECKSUM(NEWID())) % 10,
--       DATEADD( dd, ABS(CHECKSUM(NEWID())) % 120, '2019'),
--       1
--FROM sys.all_columns a, sys.all_columns b;

CREATE TABLE c(
    [Date]  date,
    [Week]  AS DATEPART( wk, [Date])
)
INSERT INTO c([Date])
SELECT  DATEADD( dd, ROW_NUMBER() OVER( ORDER BY (SELECT NULL))-1, '2019')
FROM sys.all_columns;

Отсюда я мог бы запустить ваш оригинальный запрос, который дает ожидаемые результаты, всего 1 на неделе 10.

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

SELECT
    r.customer_id
    ,COUNT( CASE WHEN c.[Week] =  9 THEN r.id END) AS wk9
    ,COUNT( CASE WHEN c.[Week] = 10 THEN r.id END) AS wk10
    ,COUNT( CASE WHEN c.[Week] = 11 THEN r.id END) AS wk11
    ,COUNT( CASE WHEN c.[Week] = 12 THEN r.id END) AS wk12
    ,COUNT( CASE WHEN c.[Week] = 14 THEN r.id END) AS wk14
    ,COUNT( CASE WHEN c.[Week] = 15 THEN r.id END) AS wk15
    ,COUNT( CASE WHEN c.[Week] = 16 THEN r.id END) AS wk16
    ,COUNT( CASE WHEN c.[Week] = 17 THEN r.id END) AS wk17
FROM r
JOIN c ON r.Create_date = c.Date
WHERE Is_ride = 1
AND (Create_date_int BETWEEN 20190302 AND 20190319
OR Create_date_int BETWEEN 20190406 AND 20190426) 
GROUP BY r.customer_id
ORDER BY r.customer_id;

Это также дает правильную информацию.

Оба эти метода могут выиграть от преагрегация для улучшения их производительности.

WITH ctePreAggregate AS(
    SELECT
      r.customer_id
     ,c.[Week]
     ,COUNT(*) AS weeklycount
    FROM r
    JOIN c
      ON r.Create_date = c.Date
    WHERE Is_ride = 1
    AND (Create_date_int BETWEEN 20190302 AND 20190319
    OR Create_date_int BETWEEN 20190406 AND 20190426)
    GROUP BY r.customer_id
            ,c.[Week]
)
SELECT
  *
FROM ctePreAggregate
PIVOT
(
SUM(weeklycount)
FOR [Week] IN
([9], [10], [11], [12], [14], [15], [16], [17])
) AS pvt
ORDER BY customer_id;


WITH ctePreAggregate AS(
    SELECT
      r.customer_id
     ,c.[Week]
     ,COUNT(*) AS weeklycount
    FROM r
    JOIN c
      ON r.Create_date = c.Date
    WHERE Is_ride = 1
    AND (Create_date_int BETWEEN 20190302 AND 20190319
    OR Create_date_int BETWEEN 20190406 AND 20190426)
    GROUP BY r.customer_id
            ,c.[Week]
)
SELECT
    customer_id
    ,SUM( CASE WHEN [Week] =  9 THEN weeklycount ELSE 0 END) AS wk9
    ,SUM( CASE WHEN [Week] = 10 THEN weeklycount ELSE 0 END) AS wk10
    ,SUM( CASE WHEN [Week] = 11 THEN weeklycount ELSE 0 END) AS wk11
    ,SUM( CASE WHEN [Week] = 12 THEN weeklycount ELSE 0 END) AS wk12
    ,SUM( CASE WHEN [Week] = 14 THEN weeklycount ELSE 0 END) AS wk14
    ,SUM( CASE WHEN [Week] = 15 THEN weeklycount ELSE 0 END) AS wk15
    ,SUM( CASE WHEN [Week] = 16 THEN weeklycount ELSE 0 END) AS wk16
    ,SUM( CASE WHEN [Week] = 17 THEN weeklycount ELSE 0 END) AS wk17
FROM ctePreAggregate
GROUP BY customer_id
ORDER BY customer_id;

Единственная проблема заключается в том, что вам нужно будет добавить серию ISNULL () в список столбцов, чтобы отображать NULL вместо нулей для запроса PIVOT.

...