2016-11-29 2 views
0

EDIT: Я перечислил оставшуюся часть КодексаЯ пытаюсь сортировать массив в SQL Server По месяцу

массив выглядит как [август], [ноября], [Октябрь], [сентября].

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

Это код, который я использую.

ALTER PROCEDURE [dbo].[CustomerFlowByMonth] 
@start datetime, 
@end datetime 

as 
declare @day varchar(max) = '' 
select @day = @day +','+'['+CONVERT(varchar(3),DATENAME(MONTH,tdate))+']' 
FROM someDB.[dbo].[someTable] 
Where tdate between @start and @end 
group by DATENAME(MONTH,tdate) 
order by DATENAME(MONTH,tdate) 
set @day = substring(@day, 2, (len(@day))) 

--select @day 
declare @query varchar(max) = 
' 
select * 
from 
(
SELECT Customer, CONVERT(varchar(3),DATENAME(MONTH,tdate)) AS OrderMonth 
,SUM(saleAmount) as amount 
    FROM someDB.[dbo].[SomeTable] 
    Where tdate between '+''''+ convert(varchar,@start) +''' and '''+convert(varchar,@end) +''' 
    group by Customer, CONVERT(varchar(3),DATENAME(MONTH,tdate)) 
) as pp 
--order by Customer, DATENAME(MONTH,tdate) 
pivot (sum(amount) for OrderMonth in ('[email protected]+')) as total' 

--print @query 
execute (@query) 

Я не лучший в SQL Server, так что любая помощь будет принята с благодарностью

ответ

0

Проблема у Вас есть то, что вы сортировки по имени строки месяц, поэтому вполне естественно, что он должен следуйте буквенному порядку, если вы хотите его в хронологическом порядке, вам нужно отсортировать по месячному номеру (DATEPART(MONTH, tDate)), что просто означает добавить это к GROUP BY, чтобы вы могли сортировать по нему. Это не повлияет на мощность, так как любое заданное имя месяца может содержать только один месяц.

При этом не следует использовать назначение переменной для объединения строк, it is not guaranteed to return the correct results, не говоря уже о порядке выполнения вами. Фактический результат, который вы закончите, будет зависеть от внутренних путей, предпринятых оптимизатором. Вместо этого используйте FOR XML PATH().

DECLARE @start DATETIME = '2016-08-03 00:00', 
     @end DATETIME = '2016-11-08 00:00', 
     @day VARCHAR(MAX) = ''; 


SET @day = STUFF((SELECT ',[' + LEFT(DATENAME(MONTH, tDate), 3) + ']' 
       FROM someDB.[dbo].[someTable] 
       WHERE tdate BETWEEN @start AND @end 
       GROUP BY LEFT(DATENAME(MONTH, tDate), 3), DATEPART(MONTH, tDate) 
       ORDER BY DATEPART(MONTH, tDate) 
       FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); 

SELECT @day; 

Стоит отметить, что вы не можете получить результаты, которые вы хотите, если диапазон дат занимает несколько лет, и это личные предпочтения, но я склонен держаться подальше от BETWEEN для диапазонов дат for reasons set out here

Если вы собираетесь строить и выполнять динамический SQL, как это, я бы advise you use sp_executesql, так что вы можете передать ваши параметры с соответствующими типами, вместо того, чтобы сделать эту ужасную конкатенацию, чтобы применить фильтры даты:

'+''''+ convert(varchar,@start) +''' and ' 

вместо этого вы можете просто декларируют и проходят paramters:

DECLARE @query NVARCHAR(MAX) = 'SELECT ... WHERE tDate BETWEEN @start AND @end'; 

EXECUTE sp_executesql @Query, N'@Start DATETIME, @end DATETIME', @Start, @end; 

NB You should always specify a length when converting to, or declaring a varchar

Наконец, при работе с DATETIME буквального формат даты xxxx-xx-xx является неоднозначным, это может означать как yyyy-MM-dd и yyyy-dd-MM, поэтому в зависимости от в региональных настройках ваш начальный параметр может быть как 3 августа, так и 8 марта. Только Формат инварианта культуры для DATETIME is yyyyMMdd. Для получения дополнительной чтения см Bad habits to kick : mis-handling date/range queries

Таким образом, ваш полный запрос может выглядеть примерно так:

DECLARE @start DATETIME = '20160803 00:00', 
     @end DATETIME = '20161108 00:00', 
     @day VARCHAR(MAX) = ''; 


SET @day = STUFF((SELECT ',[' + LEFT(DATENAME(MONTH, tDate), 3) + ']' 
       FROM someDB.[dbo].[someTable] 
       WHERE tdate BETWEEN @start AND @end 
       GROUP BY LEFT(DATENAME(MONTH, tDate), 3), DATEPART(MONTH, tDate) 
       ORDER BY DATEPART(MONTH, tDate) 
       FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); 

DECLARE @Query NVARCHAR(MAX) = 
    'SELECT * 
    FROM (SELECT Customer, LEFT(DATENAME(MONTH,tdate), 3) AS OrderMonth, SUM(saleAmount) AS Amount 
      FROM SomeDB.[dbo].[SomeTable] 
      WHERE tdate >= @start 
      AND tDate <= @end 
      GROUP BY Customer, LEFT(DATENAME(MONTH,tdate), 3)) AS pp 
     PIVOT (SUM(Amount) FOR OrderMonth IN (' + @day + ')) AS pvt'; 

EXECUTE sp_executesql @Query, N'@Start DATETIME, @end DATETIME', @Start, @end; 

Я не ставил, чтобы связать так много статей Аарона бертрановских, просто так получилось, что вы попали в много ловушек, о которых он писал в блогах. Таким образом, хотя я могу закончить ссылку на full list of his "Bad Habits to Kick" articles, они заслуживают внимания.

+0

Благодарим за быстрый ответ. Цель состоит в том, чтобы иметь столбец «Ряд клиентов» и столбец «Месяцы». Лет нет и проблема. Это будет выглядеть Ян февраля Cust A 500 200 Каст B 200 300 –

+0

Я извиняюсь, что мой вопрос не был завершен. Я добавил остальную часть кода, если это может быть любая помощь. Заранее спасибо. –

+0

Удивительные объяснения. Вопрос, который у меня есть, - DateKey? Что это такое. У меня просто красное подчеркивание для этого. –

0

ваша проблема заказ по

order by CONVERT(varchar(3),DATENAME(MONTH,tdate)) 

это прикажет даты в алфавитном порядке месячного. (Например, апрель-первых)

Вместо того, самой даты

order by tdate 
0

Ваше решение должно быть

ORDER BY MONTH(tDate) 

MONTH (Transact-SQL)

Возвращает число, представляющее месяц указанную дату.

Смежные вопросы