У меня есть два запроса, которые отлично работают:
DECLARE @StartDate DATETIME = '2021-11-01 00:00:00';
DECLARE @EndDate DATETIME = '2022-03-16 23:59:59';
DECLARE @SalesEstimateTransactionTypeId INT = 16;
DECLARE @SalesOrderTransactionTypeId INT = 15;
SELECT
DATENAME(mm, GeneralJournal.[TransactionDate]) AS ReportingMonth,
DATEPART(mm, GeneralJournal.[TransactionDate]) AS MonthNumber,
DATEPART(yyyy, GeneralJournal.[TransactionDate]) AS ReportingYear,
COUNT(TransactionId) AS TransactionCount
FROM
GeneralJournal
WHERE
GeneralJournal.[TransactionDate] >= @StartDate
AND GeneralJournal.[TransactionDate] <= @EndDate
AND MasterRecord = 1
AND TransactionTypeId = @SalesEstimateTransactionTypeId
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm,GeneralJournal.[TransactionDate]);
SELECT
DATENAME(mm, GeneralJournal.[TransactionDate]) AS ReportingMonth,
DATEPART(mm, GeneralJournal.[TransactionDate]) AS MonthNumber,
DATEPART(yyyy, GeneralJournal.[TransactionDate]) AS ReportingYear,
COUNT(DISTINCT TransactionId) AS ConversionCount
FROM
GeneralJournal
WHERE
GeneralJournal.[TransactionDate] >= @StartDate
AND GeneralJournal.[TransactionDate] <= @EndDate
AND MasterRecord = 0
AND TransactionTypeId = @SalesOrderTransactionTypeId
AND SEReferenceId > 0
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm,GeneralJournal.[TransactionDate]);
Обратите внимание, что второй запрос возвращает отдельные, потому что он может возвращать несколько значений, и мы хотим подсчитать каждое TransactionId
только один раз в этом сценарии. Они возвращают следующие результаты:
Месяц Отчетности | МесяцНомер | Отчетный год | TransactionCount |
---|---|---|---|
ноябрь | 11 | 2021 | 82 |
Декабрь | 12 | 2021 | 49 |
январь | 1 | 2022 | 64 |
февраль | 2 | 2022 | 67 |
Маршировать | 3 | 2022 | 49 |
Месяц Отчетности | МесяцНомер | Отчетный год | ConversionCount |
---|---|---|---|
ноябрь | 11 | 2021 | 42 |
Декабрь | 12 | 2021 | 27 |
январь | 1 | 2022 | 31 |
февраль | 2 | 2022 | 50 |
Маршировать | 3 | 2022 | 24 |
Мне на самом деле нужно объединить их следующим образом:
Месяц Отчетности | МесяцНомер | Отчетный год | TransactionCount | ConversionCount |
---|---|---|---|---|
ноябрь | 11 | 2021 | 82 | 42 |
Декабрь | 12 | 2021 | 49 | 27 |
январь | 1 | 2022 | 64 | 31 |
февраль | 2 | 2022 | 67 | 50 |
Маршировать | 3 | 2022 | 49 | 24 |
Я перепробовал практически все, что только мог придумать - союзы, объединения, подзапросы - но пока ничего не получается. Это самое близкое, что я могу получить:
SELECT
DATENAME(mm, GeneralJournal.[TransactionDate]) AS ReportingMonth,
DATEPART(mm, GeneralJournal.[TransactionDate]) AS MonthNumber,
DATEPART(yyyy, GeneralJournal.[TransactionDate]) AS ReportingYear,
SUM(CASE
WHEN TransactionTypeId = @SalesEstimateTransactionTypeId
AND MasterRecord = 1
THEN 1 ELSE 0
END) AS TransactionCount,
COUNT(CASE
WHEN TransactionTypeId = @SalesOrderTransactionTypeId
AND SEReferenceId > 0 THEN 1
END) AS ConversionCount
FROM
GeneralJournal
WHERE
GeneralJournal.[TransactionDate] >= @StartDate
AND GeneralJournal.[TransactionDate] <= @EndDate
AND TransactionTypeId IN (@SalesOrderTransactionTypeId, @SalesEstimateTransactionTypeId)
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm,GeneralJournal.[TransactionDate]);
Однако я не могу найти способ получить значение Distinct для ConversionCount
. В результате он возвращает полный счет:
Месяц Отчетности | МесяцНомер | Отчетный год | TransactionCount | ConversionCount |
---|---|---|---|---|
ноябрь | 11 | 2021 | 82 | 152 |
Декабрь | 12 | 2021 | 49 | 67 |
январь | 1 | 2022 | 64 | 101 |
февраль | 2 | 2022 | 67 | 136 |
Маршировать | 3 | 2022 | 49 | 64 |
Может ли кто-нибудь указать мне, как объединить два результата запроса, сохраняя при этом Distinct в подсчете конверсий? Я должен добавить, что для его работы ответ должен быть совместим как с SQL Server, так и с VistaDB, синтаксис которого является подмножеством T-SQL, потому что я обязан поддерживать оба механизма базы данных с одним и тем же запросом.
РЕДАКТИРОВАТЬ - Окончательное решение
Следуя отличному ответу Ника, я смог встроить решение в свой существующий код запроса, чтобы гарантировать наличие результатов даже в течение нескольких месяцев без записей, показанных здесь на случай, если это поможет кому-то еще:
DECLARE @StartDate DATETIME = '2021-11-01T00:00:00';
DECLARE @EndDate DATETIME = '2022-10-31T23:59:59';
DECLARE @SalesEstimateTransactionTypeId INT = 16;
DECLARE @SalesOrderTransactionTypeId INT = 15;
DECLARE @CurrentDate DATETIME;
DECLARE @Months TABLE(ReportingYear INT, MonthNumber INT, ReportingMonth VARCHAR (40));
-- Set the initial date
SET @CurrentDate = @StartDate
-- insert all dates into temp table
WHILE @CurrentDate <= @EndDate
BEGIN
INSERT INTO @Months VALUES(DATEPART(year, @CurrentDate), DATEPART(month, @CurrentDate), DATENAME(mm, @CurrentDate))
SET @CurrentDate = dateadd(mm, 1, @CurrentDate)
END;
SELECT ReportingMonth, ReportingYear, Coalesce(TransactionCount, 0) AS TransactionCount, Coalesce(ConversionCount,0) AS ConversionCount
FROM
(
SELECT months.[ReportingMonth], months.[ReportingYear], conversionData.[TransactionCount], conversionData.[ConversionCount]
FROM @Months months
LEFT JOIN
(
SELECT
ReportingMonth = DATENAME(mm, GeneralJournal.[TransactionDate]),
MonthNumber = DATEPART(mm, GeneralJournal.[TransactionDate]),
ReportingYear = DATEPART(yyyy, GeneralJournal.[TransactionDate]),
TransactionCount = SUM(CASE WHEN TransactionTypeId = @SalesEstimateTransactionTypeId AND GeneralJournal.[MasterRecord] = 1 THEN
1
ELSE
0
END
),
ConversionCount = COUNT(DISTINCT CASE WHEN GeneralJournal.[TransactionTypeId] = @SalesOrderTransactionTypeId
AND GeneralJournal.[SEReferenceId] > 0
AND GeneralJournal.[MasterRecord] = 0 THEN
GeneralJournal.[TransactionID]
END
)
FROM GeneralJournal
WHERE GeneralJournal.[TransactionDate] >= @StartDate
AND GeneralJournal.[TransactionDate] <= @EndDate
AND GeneralJournal.[TransactionTypeId] IN ( @SalesOrderTransactionTypeId, @SalesEstimateTransactionTypeId)
GROUP BY
DATEPART(yyyy, GeneralJournal.[TransactionDate]),
DATEPART(mm, GeneralJournal.[TransactionDate]),
DATENAME(mm, GeneralJournal.[TransactionDate])
) as conversionData
ON months.[ReportingYear] = conversionData.[ReportingYear] AND months.[MonthNumber] = conversionData.[MonthNumber]
) AS data;
Не используйте включающую верхнюю границу для диапазонов даты и времени — см. знакомства ответственно
Можете ли вы предоставить нам некоторые примеры подробных данных для работы?
Привет @AlwaysLearning, спасибо за советы. Обстоятельства немного сложны, так как даты передаются из приложения в форматах, перечисленных на основе данных диапазона учетных дат и параметризованных для реального запроса. Он передает ToStartOfDay и ToEndOfDay в начальной и конечной дате, и эта степень детализации слишком сложна для обработки SQL Server, поэтому ее необходимо настроить. Вы правы, хотя во всем, что вы говорите, и я очень ценю указатели.
Привет @SMor, спасибо за совет. Я работаю с предоставленными значениями, которые имеют включающую границу для просмотра человеком и передаются в качестве параметров значения. Я еще раз посмотрю, можно ли это изменить, но это нужно сделать в вызывающем коде, например. добавьте одну миллисекунду к EndOfDay, чтобы перейти к StartOfDay на следующий день, как вы рекомендуете.
Вы можете просто поместить два столбца в один и тот же запрос. Это усложняется тем, что предложения WHERE
немного отличаются. Итак, вам нужно сгруппировать, затем снова сгруппировать и использовать условную агрегацию для подсчета правильных строк для каждого столбца.
Обратите внимание на следующее:
COUNT(DISTINCT CASE
, однако обычно это медленнее, поскольку компилятор не распознает, что делает CASE
, и вместо этого выполнит полную сортировку.EOMONTH
расчету быстрее, чем группировать по целому месяцу. Вы можете вытащить год и месяц в SELECT
.COUNT(TransactionId)
вернет количество ненулевых TransactionId
значений. если TransactionId
не может быть нулевым, то COUNT(*)
— это то же самое.TransactionDate
имеет компонент времени, то вам следует использовать полуоткрытый интервал >= AND <
DECLARE @StartDate DATETIME = '2021-11-01T00:00:00';
DECLARE @EndDate DATETIME = '2022-03-17T00:00:00';
DECLARE @SalesEstimateTransactionTypeId INT = 16;
DECLARE @SalesOrderTransactionTypeId INT = 15;
SELECT
DATENAME(month, gj.mth) AS ReportingMonth,
DATEPART(month, gj.mth) AS MonthNumber,
DATEPART(year , gj.mth) AS ReportingYear,
SUM(TransactionCount) AS TransactionCount,
COUNT(CASE WHEN ConversionCount > 0 THEN 1 END) AS ConversionCount
FROM (
SELECT
EOMONTH(gj.TransactionDate) AS mth,
gj.TransactionId,
COUNT(CASE WHEN gj.MasterRecord = 1 AND gj.TransactionTypeId = @SalesEstimateTransactionTypeId THEN 1 END) AS TransactionCount,
COUNT(CASE WHEN gj.MasterRecord = 0 AND gj.TransactionTypeId = @SalesOrderTransactionTypeId AND gj.SEReferenceId > 0 THEN 1 END) AS ConversionCount
FROM GeneralJournal gj
WHERE gj.TransactionDate >= @StartDate
AND gj.TransactionDate < @EndDate
AND gj.TransactionTypeId IN (@SalesOrderTransactionTypeId, @SalesEstimateTransactionTypeId)
GROUP BY
EOMONTH(gj.TransactionDate),
TransactionId
) g
GROUP BY
mth;
Критерии разные для каждого COUNT
. Посмотрите на пункты WHERE
.
Правда, пропустил. Изменили
Ваш второй запрос, который близок, я думаю, просто имеет пару незначительных упущений.
MasterRecord = 0
в выписке ConversionCount CASE
.CASE
, вы должны вернуть TransactionID или NULL, чтобы вы могли по-прежнему подсчитывать различные значения.DISTINCT
в вашем счетчике конверсий COUNT
.COUNT
. Я предполагал, что у вас всегда будет одна или несколько NULL
, поэтому я просто вычитаю 1 из COUNT(DISTINCT ...)
, чтобы компенсировать это.(Я не могу быть на 100% в синтаксисе здесь без некоторых примеров подробных данных для работы.)
SELECT
ReportingMonth = DATENAME(mm, GeneralJournal.TransactionDate),
MonthNumber = DATEPART(mm, GeneralJournal.TransactionDate),
ReportingYear = DATEPART(yyyy, GeneralJournal.TransactionDate),
TransactionCount = SUM(CASE
WHEN TransactionTypeId = @SalesEstimateTransactionTypeId
AND MasterRecord = 1 THEN
1
ELSE
0
END
),
ConversionCount = COUNT(DISTINCT CASE
WHEN TransactionTypeId = @SalesOrderTransactionTypeId
AND SEReferenceId > 0
AND MasterRecord = 0 THEN
TransactionID
ELSE
NULL
END
) - 1 /* Subtract 1 for the NULL */
FROM GeneralJournal
WHERE
GeneralJournal.TransactionDate >= @StartDate
AND GeneralJournal.TransactionDate <= @EndDate
AND TransactionTypeId IN (
@SalesOrderTransactionTypeId,
@SalesEstimateTransactionTypeId
)
GROUP BY
DATEPART(yyyy, GeneralJournal.TransactionDate),
DATEPART(mm, GeneralJournal.TransactionDate),
DATENAME(mm, GeneralJournal.TransactionDate);
Привет, Ник, спасибо за этот ответ. Вы заметили мои ошибки, и я узнал кое-что новое, поскольку я никогда раньше не видел синтаксиса псевдоним = выражение и всегда использовал выражение как псевдоним. Единственное изменение, которое мне нужно было сделать, это удалить ноль и соответствующий вывод. Поскольку мы подсчитываем только те, которые совпадают, в каждом диапазоне дат возвращались значения на единицу меньше, чем должны были быть. Я вернул его обратно в код, который у меня уже есть, который позволяет месяцами не проводить транзакции и по-прежнему возвращает ноль, и все выглядит идеально. Большое спасибо за отличный ответ.
Я предпочитаю формат ColumnName =, потому что он делает ваши псевдонимы столбцов легко читаемыми и упорядоченными, когда у вас сложный вычисляемый столбец.
В сторону... У меня есть два запроса, которые отлично работают... в очень специфических обстоятельствах. Попытка использовать
'2021-11-01 00:00:00'
для даты и времени даст 01 ноября 2021 года, когда действуетset dateformat mdy
, но 11 января 2021 года, когда действуетset dateformat dmy
. Если вы хотите быть независимым от формата даты, вам нужно использовать разделительT
, например:'2021-11-01T00:00:00'
.