Escassamente povoada tabela de pesquisa em SQL
Pergunta
Eu estou tentando escrever um método de pesquisa para determinar uma mensagem SMS para enviar para um usuário com base em alguns parâmetros associados com o usuário / sistema. Teremos uma mensagem padrão que será usado como um último recurso, mas existem várias maneiras de substituir a mensagem por vários parâmetros. Aqui está o que eu tenho até agora para a consulta de pesquisa - existem melhores maneiras de fazer isso? Talvez uma pesquisa não é a abordagem correta para isso?
Aqui está a tabela de referência:
MessageLookup
{
ID bigint PK
Key varchar
CampaignTypeID bigint FK,
ServiceProviderID bigint FK nullable, -- optional override parameter
DistributorID bigint FK nullable, -- optional override parameter
CampaignID bigint FK nullable, -- optional override parameter
Message varchar
}
Aqui está um exemplo do que a mesa seria parecido com:
ID Key CTID SPID DistID CampID Message
1 Help 1 NULL NULL NULL 'This is the default message'
2 Help 1 375 NULL NULL 'This is the SP375 message'
3 Help 1 377 NULL NULL 'This is the SP377 message'
4 Help 1 NULL 13 NULL 'This is the Dist13 message'
5 Help 1 375 13 NULL 'This is the SP375/Dist13 message'
6 Help 1 NULL 13 500 'This is the Dist13/Camp500 message'
7 Help 1 375 13 500 'This is the SP375/Dist13/Camp500 msg'
8 Help 1 NULL NULL 500 'This is the Camp500 help message'
Aqui está a consulta que tenho:
select
--top 1
*
from MessageLookup ml
where ml.[Key] = @Key
and ml.CampaignTypeID = @CampaignTypeID
and
(
ml.ServiceProviderID = @ServiceProviderID or
ml.ServiceProviderID is null
)
and
(
ml.DistributorID = @DistributorID or
ml.DistributorID is null
)
and
(
ml.CampaignID = @CampaignID or
ml.CampaignID is null
)
order by
CampaignID desc, -- highest precedence lookup param
DistributorID desc,
ServiceProviderID desc -- lowest precedence lookup param
Solução
Eu não tenho certeza que a melhor maneira é, mas aqui estão algumas alternativas:
Um pensamento seria para armazenar um padrão com cada regra, assim:
ID Key CTID Rule Message
1 Help 1 '[%:%:%]' 'This is the default message'
2 Help 1 '[375:%:%]' 'This is the SP375 message'
3 Help 1 '[377:%:%]' 'This is the SP377 message'
4 Help 1 '[%:13:%]' 'This is the Dist13 message'
5 Help 1 '[375:13:%]' 'This is the SP375/Dist13 message'
e, em seguida, usar um teste como em vez de todas as ANDs.
Outro pensamento seria usar junções externas.
Or (jogando fora a resposta que acabou de chegar) para secar as coisas mais escrevendo:
where ml.[Key] = @Key
and ml.CampaignTypeID = @CampaignTypeID
and IsNull(ml.ServiceProviderID = @ServiceProviderID,true)
and IsNull(ml.DistributorID = @DistributorID, true)
and IsNull(ml.CampaignID = @CampaignID, true)
Outras dicas
Eu acho que é uma abordagem válida, fácil de estender, a intenção é muito clara, e você pode arrumar o sql fazendo o seguinte
select
--top 1
*
from MessageLookup ml
where ml.[Key] = @Key
and ml.CampaignTypeID = @CampaignTypeID
and ml.ServiceProviderID = IsNull(@ServiceProviderID, ml.ServiceProviderID)
and ml.DistributorID = IsNull(@DistributorID, ml.DistributorID)
and ml.CampaignID = IsNull(@CampaignID, ml.CampaignID)
....
O que você está fazendo faz sentido, e obras. Se você é depois de melhores práticas - não usar "SELECT *" -. Enumerar as colunas que você está selecionando
Eu acho que eu iria projetar o banco de dados de uma forma diferente, com uma mesa TA que seria (MSGID, Key, CTID, Mensagem) e outra TB que iria armazenar (MSGID, ID, idtype), onde ID representaria CampID / DistId / DefaultId (indicado por idtype), a PK dos quais deve ser (ID, idtype, MSGID), por esta ordem. Você pode atribuir a idtype um valor numérico que indica a prioridade, com 0 para o padrão (e uma ID correspondente de 0). Todas as colunas não são NULL.
Se eu entendi bem o seu problema, sua entrada é feita de três valores x, yez (mais um implícito 0 no meu caso), você quer retornar a mensagem para o qual tem o maior número de partidas, e, no caso da igualdade, ordem por idtype.
select MSGID, count(*) as nbr_candidates, max(IDTYPE) as priority
from TB
where (ID = x and IDTYPE = ...)
or (ID = y and IDTYPE = ...)
or (ID = z and IDTYPE = ...)
or (ID = 0 and IDTYPE = 0)
group by MSGID
order by 2 desc, 3 desc
deve retornar o "melhor mensagem" como sua primeira linha, e tudo que você precisa fazer é adicionar um
top 1
, em seguida, juntar-se com a outra mesa. Este é provável que seja mais rápido do que uma solução one-mesa, porque a tabela TB contém apenas IDs numéricos e será bastante compacto, e a junção será instantânea.