Estou tentando agrupar registros por semana, armazenando a data agregada como o primeiro dia da semana. No entanto, a técnica padrão que uso para arredondar as datas não parece funcionar corretamente com semanas (embora funcione para dias, meses, anos, trimestres e qualquer outro período de tempo que eu apliquei).
Aqui está o SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
Isso retorna 2011-08-22 00:00:00.000
, que é uma segunda-feira, não um domingo. Selecionando @@datefirst
retorna 7
, que é o código de domingo, para que o servidor esteja configurado corretamente até onde eu sei.
Posso contornar isso facilmente alterando o código acima para:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
Mas o fato de ter de fazer essa exceção me deixa um pouco inquieto. Além disso, desculpas se esta for uma pergunta duplicada. Encontrei algumas questões relacionadas, mas nenhuma que abordasse este aspecto especificamente.
fonte
(@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7
permanece constante, independentemente da@@datefirst
configuração, eu acho. Com segunda-feira = 2.Respostas:
Para responder por que você está recebendo uma segunda-feira e não um domingo:
Você está adicionando um número de semanas à data 0. O que é a data 0? 1900-01-01. Qual foi o dia 01/01/1900? Segunda-feira. Então, em seu código, você está dizendo, quantas semanas se passaram desde segunda-feira, 1º de janeiro de 1900? Vamos chamar isso de [n]. Ok, agora adicione [n] semanas a segunda-feira, 1 ° de janeiro de 1900. Você não deve se surpreender que isso acaba sendo uma segunda-feira.
DATEADD
não tem ideia de que você deseja adicionar semanas, mas apenas até chegar a um domingo, é apenas adicionar 7 dias, depois adicionar mais 7 dias, ... assim comoDATEDIFF
apenas reconhece os limites que foram ultrapassados. Por exemplo, ambos retornam 1, embora algumas pessoas reclamem que deve haver alguma lógica sensata embutida para arredondar para cima ou para baixo:Para responder como conseguir um domingo:
Se você quiser um domingo, escolha uma data base que não seja segunda-feira, mas sim um domingo. Por exemplo:
Isso não será interrompido se você alterar sua
DATEFIRST
configuração (ou se seu código estiver sendo executado para um usuário com uma configuração diferente) - desde que você ainda queira um domingo, independentemente da configuração atual. Se você quer que essas duas respostas para jive, então você deve usar uma função que não dependem daDATEFIRST
configuração, por exemplo,Portanto, se você alterar sua
DATEFIRST
configuração para segunda, terça-feira, o que for, o comportamento mudará. Dependendo de qual comportamento você deseja, você pode usar uma destas funções:...ou...
Agora, você tem muitas alternativas, mas qual tem o melhor desempenho? Ficaria surpreso se houvesse grandes diferenças, mas reuni todas as respostas fornecidas até agora e as executei em dois conjuntos de testes - um barato e outro caro. Eu medi as estatísticas do cliente porque não vejo a E / S ou a memória desempenhando um papel no desempenho aqui (embora elas possam ser importantes dependendo de como a função é usada). Em meus testes, os resultados são:
Consulta de atribuição "barata":
Consulta de atribuição "cara":
Posso retransmitir os detalhes dos meus testes, se desejar - parando aqui, pois isso já está ficando muito prolixo. Fiquei um pouco surpreso ao ver o Curt sair como o mais rápido no topo, devido ao número de cálculos e código embutido. Talvez eu execute alguns testes mais completos e blog sobre isso ... se vocês não têm nenhuma objeção a eu publicar suas funções em outro lugar.
fonte
Para aqueles que precisam obter:
Segunda-feira = 1 e Domingo = 7:
Domingo = 1 e sábado = 7:
Acima havia um exemplo semelhante, mas graças ao dobro "% 7" seria muito mais lento.
fonte
select (datediff(dd,5,cal.D_DATE)%7 + 1)
eselect (datediff(dd,6,cal.D_DATE)%7 + 1)
Para quem precisa da resposta no trabalho e a função de criação é proibida pelo seu DBA, a seguinte solução funcionará:
Isso dá o início da semana. Aqui, presumo que os domingos são o início das semanas. Se você acha que segunda-feira é o começo, você deve usar:
fonte
Isso funciona maravilhosamente bem para mim:
fonte
Pesquisei este script:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307
fonte
Talvez você precise disso:
Ou
Função
fonte
DATEPART(DW
depende de@@datefirst
DATE
então você não tem que fazer nenhuma conversão subótima deVARCHAR
ida e volta apenas para retirar qualquer componente de tempo acidental que seja passado.Time
valores.fonte
Para o básico (domingo da semana atual)
Se na semana anterior:
Internamente, construímos uma função que faz isso, mas se você precisar rápido e sujo, esta servirá.
fonte
Como a data juliana 0 é uma segunda-feira, basta adicionar o número de semanas ao domingo, que é o dia anterior a -1. selecionar dateadd (wk, datediff (wk, 0, getdate ()), - 1)
fonte
Essa é minha lógica. Defina o primeiro da semana como segunda-feira, calcule qual é o dia da semana em que um determinado dia é e, em seguida, usando DateAdd e Case, calculo qual seria a data na segunda-feira anterior daquela semana.
fonte
Não tenho problemas com nenhuma das respostas fornecidas aqui, no entanto, acho que a minha é muito mais simples de implementar e entender. Eu não executei nenhum teste de desempenho nele, mas deve ser negligenciável.
Portanto, deduzi minha resposta do fato de que as datas são armazenadas no servidor SQL como números inteiros (estou falando apenas sobre o componente de data). Se você não acredita em mim, tente este SELECT CONVERT (INT, GETDATE ()) e vice-versa.
Agora, sabendo disso, você pode fazer algumas equações matemáticas interessantes. Você pode encontrar um melhor, mas aqui está o meu.
fonte
@@DATEFIRST
também é 7, mas se@testDate
for o início da semana, isso retornará uma data que é o dia anterior.Eu tive um problema parecido. Dado uma data, eu queria obter a data da segunda-feira daquela semana.
Usei a seguinte lógica: encontre o número do dia da semana no intervalo de 0-6 e subtraia-o da data de origem.
Usei: DATEADD (dia, - (DATEPART (dia da semana,) + 5)% 7,)
Como DATEPRRT (dia da semana,) retorna 1 = Domingo ... 7 = Sábado, DATEPART (dia da semana,) + 5)% 7 retorna 0 = Segunda ... 6 = Domingo.
Subtrair esse número de dias da data original resulta na segunda-feira anterior. A mesma técnica pode ser usada em qualquer dia inicial da semana.
fonte
Eu achei isso simples e útil. Funciona mesmo se o primeiro dia da semana for domingo ou segunda-feira.
DECLARAR @BaseDate AS Data
SET @BaseDate = GETDATE ()
DECLARAR @FisrtDOW AS Data
SELECT @FirstDOW = DATEADD (d, DATEPART (WEEKDAY, @ BaseDate) * -1 + 1, @BaseDate)
fonte
Talvez eu esteja simplificando demais aqui, e pode ser o caso, mas isso parece funcionar para mim. Ainda não tive problemas com ele ...
fonte
SET DATEFIRST
.DATEFIRST
nada (há três anos e meio), e ainda não menciona . E você também deve evitar formatos regionais comom/d/y
, mesmo em cenários onde m e d são iguais.