Tenho tabelas com milhões (possivelmente bilhões) de linhas, então realmente preciso ser eficiente com a consulta.
Nesta consulta, estou unindo várias tabelas. O segmento em questão é:
LEFT JOIN
dbo.GCSOCTPS dbo_GCSOCTPS ON (GC_TBMED.MED_CLASS_NUM = dbo_GCSOCTPS.CLASS_NUM)
AND (GC_TBMED.MED_SOC_NUM = dbo_GCSOCTPS.SOC_NUM)
AND (GC_TBMED.MED_EFF_DATE = dbo_GCSOCTPS.EFF_DATE)
AND (GC_TBMED.MED_CANC_DATE = dbo_GCSOCTPS.CANC_DATE))
GC_TBMED
tem datas no DATE
formato, dbo_GCSOCTPS
tem datas no DATETIME
formato. Infelizmente, devido à forma como nossa empresa usa dados, não posso alterar isso.
Qual seria a maneira mais eficiente de comparar essas colunas? CAST
? CONVERT
? Já vi pessoas convertendo para o formato de texto e comparando dessa forma. Espero que alguém possa me orientar com base em sua experiência com conjuntos de dados muito grandes.
Se eu executar este bloco de código, obtenho um valor para HDHPQ:
SELECT TOP 200
HDHPQ,
SOC_NUM,
EFF_DATE,
CLASS_NUM,
CANC_DATE
FROM
dbo.GCSOCTPS
WHERE
SOC_NUM = '25521'
AND CLASS_NUM = '37'
AND CANC_DATE IS NULL;
Isto é retornado:
HDHPQ SOC_NUM EFF_DATE CLASS_NUM CANC_DATE
N 25521 2025-01-01 00:00:00.000 37 NULL
Se eu executar esse bloco de código, também receberei dados retornados:
SELECT TOP 200
MED_SOC_NUM,
MED_EFF_DATE,
MED_CLASS_NUM,
MED_CANC_DATE
FROM
[dbo].[AS_tblTBMED] GC_TBMED
WHERE
GC_TBMED.MED_SOC_NUM = '25521'
AND GC_TBMED.MED_CLASS_NUM = '37'
AND GC_TBMED.MED_CANC_DATE IS NULL;
Isso é retornado:
MED_SOC_NUM MED_EFF_DATE MED_CLASS_NUM MED_CANC_DATE
25521 2025-01-01 37 NULL
Cada um deles retorna uma linha. Preciso juntá-los para obter todos os dados da segunda consulta e o valor de HDHPQ da primeira consulta.
Então eu executo esta consulta:
SELECT DISTINCT TOP 200
dbo_GCSOCTPS.HDHPQ,
dbo_GCSOCTPS.SOC_NUM,
dbo_GCSOCTPS.EFF_DATE,
dbo_GCSOCTPS.CLASS_NUM,
dbo_GCSOCTPS.CANC_DATE,
GC_TBMED.MED_SOC_NUM,
GC_TBMED.MED_EFF_DATE,
GC_TBMED.MED_CLASS_NUM,
GC_TBMED.MED_CANC_DATE
FROM
[dbo].[AS_tblTBMED] GC_TBMED
LEFT JOIN
dbo.GCSOCTPS dbo_GCSOCTPS ON (GC_TBMED.MED_CLASS_NUM = dbo_GCSOCTPS.CLASS_NUM)
AND (GC_TBMED.MED_SOC_NUM = dbo_GCSOCTPS.SOC_NUM)
AND (GC_TBMED.MED_EFF_DATE = CAST(dbo_GCSOCTPS.EFF_DATE as DATE))
AND (GC_TBMED.MED_CANC_DATE = CAST(dbo_GCSOCTPS.CANC_DATE as DATE))
WHERE
GC_TBMED.MED_SOC_NUM = '25521'
AND GC_TBMED.MED_CLASS_NUM = '37'
AND GC_TBMED.MED_CANC_DATE IS NULL
AND dbo_GCSOCTPS.EFF_DATE >= '2025-01-01';
E um conjunto de registros vazio é retornado. Se eu comentar as duas datas na junção, obtenho dados. Portanto, presumo que os campos de data não estejam sendo equalizados corretamente, já que são iguais e, portanto, eu deveria obter dados se eles fossem incluídos na consulta.
Você tem (pelo menos) dois problemas aqui:
<anything> = NULL
nunca serão verdadeiros, então a junção falhará.Você provavelmente precisa de algo como:
Mas há mais algumas linhas suspeitas:
CAST(gcs.EFF_DATE as DATE)
se EFF_DATE não contiver horas etc., você pode pular o elenco provavelmentetbm.MED_CLASS_NUM = '37'
por que você usa'37'
e não apenas37
para colunas que parecem ser um número?Para esses tipos de dados antigos, eu usaria cast
Não vejo por que isso acrescentaria alguma sobrecarga.
o que o torna eficiente? Índices
então tudo o que você precisa fazer é deixá-los indexáveis.
o que significa que você não pode usar nenhuma função wrapper em torno de seus campos de data e hora (como convertê-los em string ou inteiro)
há algumas exceções inofensivas como DateAdd(). enquanto busca intervalo, ainda é intervalo.
então usando
DateTime >= Date AND DateTime < DATEADD(DAY, 1, Date)
está bem..
e eu nunca usaria nada como;
WHERE CAST(DateTimeColumn AS DATE) = DateColumn
Como outros já mencionaram, são os NULLs para CANC_DATE nos seus dados que estão impedindo o seu retorno. Tente converter NULLs como um valor de data real: