Tabla de búsqueda escasamente poblada en SQL
Pregunta
Estoy tratando de escribir un método de búsqueda para determinar un mensaje SMS para enviar a un usuario en función de algunos parámetros asociados con el usuario / sistema. Tendremos un mensaje predeterminado que se utilizará como último recurso, pero hay varias formas de anular el mensaje mediante varios parámetros. Esto es lo que tengo hasta ahora para la consulta de búsqueda: ¿hay alguna forma mejor de hacer esto? ¿Quizás una búsqueda no es el enfoque correcto para esto?
Aquí está la tabla de búsqueda:
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
}
Aquí hay un ejemplo de cómo se vería la tabla:
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'
Aquí está la consulta que tengo:
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
Solución
No estoy seguro de cuál es la mejor manera, pero aquí hay algunas alternativas:
Un pensamiento sería almacenar un patrón con cada regla, así:
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'
y luego use una prueba LIKE en lugar de todos los AND.
Otra idea sería utilizar OUTER JOINS.
O (reproduciendo la respuesta que acaba de llegar) para SECAR aún más las cosas escribiendo:
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)
Otros consejos
Creo que es un enfoque válido, fácil de extender, la intención es bastante clara, y puede ordenar el sql haciendo lo siguiente
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)
....
Lo que estás haciendo tiene sentido y funciona. Si persigue las mejores prácticas, no use " SELECT * " - enumere qué columnas está seleccionando.
Creo que diseñaría la base de datos de una manera diferente, con una tabla TA que sería (MSGID, Clave, CTID, Mensaje) y otra TB que almacenaría (MSGID, ID, IDTYPE) donde ID representaría CampID / DistId / DefaultId (indicado por IDTYPE), cuyo PK debe ser (ID, IDTYPE, MSGID) en este orden. Puede asignar a IDTYPE un valor numérico que indique prioridad, con 0 para el valor predeterminado (y una ID coincidente de 0). Todas las columnas NO SON NULAS.
Si entiendo bien su problema, su entrada se compone de tres valores x, y y z (más un 0 implícito en mi caso), desea devolver el mensaje para el que tiene la mayor cantidad de coincidencias y, en caso de igualdad, ordenado 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
debería devolver el " mejor mensaje " como su primera fila, y todo lo que necesita agregar es un
top 1
luego únete a la otra tabla. Es probable que esto sea más rápido que una solución de una tabla, porque la tabla TB solo contiene ID numéricos y será bastante compacta, y la unión será instantánea.