#tsql #subquery #distinct #aggregate-functions
#tsql #подзапрос #distinct #агрегатные функции
Вопрос:
SELECT
CAST(c.DT AS DATE) AS 'Date'
, COUNT(p.PatternID) AS 'Count'
FROM CalendarMain c
LEFT OUTER JOIN Pattern p
ON c.DT = p.PatternDate
INNER JOIN Result r
ON p.PatternID = r.PatternID
INNER JOIN Detail d
ON p.PatternID = d.PatternID
WHERE r.Type = 7
AND d.Panel = 501
AND CAST(c.DT AS DATE)
BETWEEN '20190101' AND '20190201'
GROUP BY CAST(c.DT AS DATE)
ORDER BY CAST(c.DT AS DATE)
Приведенный выше запрос у меня не работает. Он по-прежнему пропускает дни, когда количество равно нулю, для его c.DT.
c.DT и p.PatternDate оба являются временем DateTime, хотя c.DT не может быть нулевым. На самом деле это PK для таблицы. Он заполняется как DateTimes для каждого отдельного дня с 2015 по 2049 год, поэтому записи за эти дни существуют.
Еще одна странная вещь, которую я заметил, это то, что при присоединении к C.DT = p.PatternDate вообще ничего не возвращается без приведения или ПРЕОБРАЗОВАНИЯ в стиль даты. Не уверен, почему, когда они оба являются DateTimes.
Ответ №1:
Здесь есть о чем поговорить. На данном этапе неясно, что вы на самом деле пытаетесь посчитать. Если это количество «шаблонов» в день за январь 2019 года, то:
- Ваш BETWEEN также будет учитывать любую активность, происходящую 1 февраля,
- Похоже, что один шаблон может иметь несколько результатов, что потенциально приводит к ошибочному подсчету
- Похоже, что один шаблон может содержать несколько деталей, что потенциально может привести к ошибочному подсчету
- Если в одном шаблоне есть, скажем, 3 подходящих результата, а также 4 детали, вы получите их перекрестный продукт. Ваше количество будет равно 12.
Я собираюсь предположить:
- вам нужно только определенное количество шаблонов, независимо от количества деталей и результатов.
- Вам нужна только активность за январь
--Set up some dummy data
DROP TABLE IF EXISTS #CalendarMain
SELECT cast('20190101' as datetime) as DT
INTO #CalendarMain
UNION ALL SELECT '20190102' as DT
UNION ALL SELECT '20190103' as DT
UNION ALL SELECT '20190104' as DT
UNION ALL SELECT '20190105' as DT --etc etc
;
DROP TABLE IF EXISTS #Pattern
SELECT cast('1'as int) as PatternID
,cast('20190101 13:00' as datetime) as PatternDate
INTO #Pattern
UNION ALL SELECT 2,'20190101 14:00'
UNION ALL SELECT 3,'20190101 15:00'
UNION ALL SELECT 4,'20190104 11:00'
UNION ALL SELECT 5,'20190104 14:00'
;
DROP TABLE IF EXISTS #Result
SELECT cast(100 as int) as ResultID
,cast(1 as int) as PatternID
,cast(7 as int) as [Type]
INTO #Result
UNION ALL SELECT 101,1,7
UNION ALL SELECT 102,1,8
UNION ALL SELECT 103,1,9
UNION ALL SELECT 104,2,8
UNION ALL SELECT 105,2,7
UNION ALL SELECT 106,3,7
UNION ALL SELECT 107,3,8
UNION ALL SELECT 108,4,7
UNION ALL SELECT 109,5,7
UNION ALL SELECT 110,5,8
;
DROP TABLE IF EXISTS #Detail
SELECT cast(201 as int) as DetailID
,cast(1 as int) as PatternID
,cast(501 as int) as Panel
INTO #Detail
UNION ALL SELECT 202,1,502
UNION ALL SELECT 203,1,503
UNION ALL SELECT 204,1,502
UNION ALL SELECT 205,1,502
UNION ALL SELECT 206,1,502
UNION ALL SELECT 207,2,502
UNION ALL SELECT 208,2,503
UNION ALL SELECT 209,2,502
UNION ALL SELECT 210,4,502
UNION ALL SELECT 211,4,501
;
-- create some variables
DECLARE @start_date as date = '20190101';
DECLARE @end_date as date = '20190201'; --I assume this is an exclusive end date
SELECT cal.DT
,isnull(patterns.[count],0) as [Count]
FROM #CalendarMain cal
LEFT JOIN ( SELECT cast(p.PatternDate as date) as PatternDate
,COUNT(DISTINCT p.PatternID) as [Count]
FROM #Pattern p
JOIN #Result r ON p.PatternID = r.PatternID
JOIN #Detail d ON p.PatternID = d.PatternID
WHERE r.[Type] = 7
and d.Panel = 501
GROUP BY cast(p.PatternDate as date)
) patterns ON cal.DT = patterns.patternDate
WHERE cal.DT >= @start_date
and cal.DT < @end_date --Your code would have included 1 Feb, which I assume was unintentional.
ORDER BY cal.DT
;