Pergunta

Eu tenho uma tabela grande com mais de 1 milhão de registros.Infelizmente, a pessoa que criou a tabela decidiu colocar as datas em um varchar(50) campo.

Preciso fazer uma comparação simples de datas -

datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

Mas falha no convert():

Conversion failed when converting datetime from character string.

Aparentemente há algo nesse campo que ele não gosta, e como há tantos registros, não posso dizer só de olhar.Como posso higienizar adequadamente todo o campo de data para que não falhe no convert()?Aqui está o que tenho agora:

select count(*)
from MyTable
where
    isdate(lastUpdate) > 0
    and datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

@SQLMenace

Não estou preocupado com o desempenho neste caso.Esta será uma consulta única.Alterar a tabela para um campo de data e hora não é uma opção.

@Jon Limjap

Tentei adicionar o terceiro argumento e não faz diferença.


@SQLMenace

O problema provavelmente é como os dados são armazenados; existem apenas dois formatos seguros;ISO AAAAMMDD;ISO 8601 aaaa-mm-dd Thh:mm:ss:mmm (sem espaços)

Não seria isdate() verifique cuidar disso?

Não preciso de 100% de precisão.Eu só quero obter a maioria dos registros dos últimos 30 dias.


@SQLMenace

select isdate('20080131') -- returns 1
select isdate('01312008') -- returns 0

@Brian Schkerke

Coloque CASE e ISDATE dentro da função CONVERT().

Obrigado!Isso resolveu.

Foi útil?

Solução

Coloque o CASE e ISDATE dentro de CONVERT() função.

SELECT COUNT(*) FROM MyTable
WHERE
    DATEDIFF(dd, CONVERT(DATETIME, CASE IsDate(lastUpdate)
        WHEN 1 THEN lastUpdate
        ELSE '12-30-1899'
    END), GetDate()) < 31

Substituir '12-30-1899' com a data padrão de sua escolha.

Outras dicas

Que tal escrever um cursor para percorrer o conteúdo, tentando converter cada entrada? Quando ocorrer um erro, imprima a chave primária ou outros detalhes de identificação para o registro do problema.Não consigo pensar em uma maneira baseada em conjunto de fazer isso.

Não totalmente baseado em set, mas se apenas 3 linhas em 1 milhão forem ruins, você economizará muito tempo

select * into BadDates
from Yourtable
where isdate(lastUpdate) = 0

select * into GoodDates
from Yourtable
where isdate(lastUpdate) = 1

então basta olhar a tabela BadDates e corrigir isso

O ISDATE() cuidaria das linhas que não foram formatadas corretamente se realmente estivessem sendo executadas primeiro.No entanto, se você observar o plano de execução, provavelmente descobrirá que o predicado DATEDIFF está sendo aplicado primeiro - portanto, a causa do seu problema.

Se você estiver usando o SQL Server Management Studio, acesse CTRL+eu para visualizar o plano de execução estimado para uma consulta específica.

Lembre-se, SQL não é uma linguagem processual e a lógica de curto-circuito pode funcionar, mas apenas se você for cuidadoso ao aplicá-la.

Que tal escrever um cursor para percorrer o conteúdo, tentando converter cada entrada?

Quando ocorrer um erro, imprima a chave primária ou outros detalhes de identificação para o registro do problema.

Não consigo pensar em uma maneira baseada em conjunto de fazer isso.

Editar - ah sim, esqueci de ISDATE().Definitivamente, uma abordagem melhor do que usar um cursor.+1 para SQLMenace.

Na sua chamada convert, você precisa especificar um terceiro parâmetro de estilo, por exemplo, o formato dos dados e horas armazenados como varchar, conforme especificado neste documento: CAST e CONVERT (T-SQL)

Imprima os registros.Dê a cópia impressa ao idiota que decidiu usar varchar(50) e peça-lhe para encontrar o registro do problema.

Da próxima vez, eles poderão entender a necessidade de escolher um tipo de dados apropriado.

O problema provavelmente é como os dados são armazenados, existem apenas dois formatos seguros

ISO AAAAMMDD

ISO 8601 aaaa-mm-dd Thh:mm:ss:mmm(sem espaços)

eles funcionarão independentemente do seu idioma.

Pode ser necessário fazer um SET DATEFORMAT YMD (ou como os dados estão armazenados) para que funcione

A verificação isdate() não cuidaria disso?

Execute isso para ver o que acontece

select isdate('20080131')
select isdate('01312008')

Tenho certeza de que alterar a tabela/coluna pode não ser uma opção devido a quaisquer requisitos do sistema legado, mas você já pensou em criar uma visualização que tenha a lógica de conversão de data incorporada, se estiver usando uma versão mais recente do sql, então você pode até usar uma visualização indexada?

Eu sugeriria limpar a bagunça e mudar a coluna para data e hora porque fazer coisas assim

WHERE datediff(dd, convert(datetime, lastUpdate), getDate()) < 31

não pode usar um índice e será muitas vezes mais lento do que se você tivesse uma coluna datetime,n e fizesse

where lastUpdate > getDate() -31

Você também precisa levar em consideração horas e segundos, é claro

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top