我有一个名为ClientUrls
的表,它具有以下结构:
+------------+----------------+----------+
| ColumnName | DataType | Nullable |
+------------+----------------+----------+
| ClientId | INT | No |
| CountryId | INT | Yes |
| RegionId | INT | Yes |
| LanguageId | INT | Yes |
| URL | NVARCHAR(2048) | NO |
+------------+----------------+----------+
我有一个存储过程up_GetClientUrls
,它接受以下参数:
@ClientId INT
@CountryId INT
@RegionId INT
@LanguageId INT
有关proc的信息
优先级矩阵(1为第一个)
+---------+----------+-----------+----------+------------+
| Ranking | ClientId | CountryId | RegionId | LanguageId |
+---------+----------+-----------+----------+------------+
| 1 | NOT NULL | NOT NULL | NOT NULL | NOT NULL |
| 2 | NOT NULL | NULL | NOT NULL | NOT NULL |
| 3 | NOT NULL | NOT NULL | NULL | NOT NULL |
| 4 | NOT NULL | NULL | NULL | NOT NULL |
| 5 | NOT NULL | NOT NULL | NOT NULL | NULL |
| 6 | NOT NULL | NULL | NOT NULL | NULL |
| 7 | NOT NULL | NULL | NULL | NULL |
+---------+----------+-----------+----------+------------+
以下是一些示例数据:
+----------+-----------+----------+------------+-------------------------------+
| ClientId | CountryId | RegionId | LanguageId | URL |
+----------+-----------+----------+------------+-------------------------------+
| 1 | 1 | 1 | 1 | http://www.Website.com |
| 1 | 1 | 1 | NULL | http://www.Otherwebsite.com |
| 1 | 1 | NULL | 2 | http://www.Anotherwebsite.com |
+----------+-----------+----------+------------+-------------------------------+
示例存储过程调用
EXEC up_GetClientUrls @ClientId = 1
,@CountryId = 1
,@RegionId = 1
,@LanguageId = 2
预期回复(基于示例数据)
+----------+-----------+----------+------------+-------------------------------+
| ClientId | CountryId | RegionId | LanguageId | URL |
+----------+-----------+----------+------------+-------------------------------+
| 1 | 1 | NULL | 2 | http://www.Anotherwebsite.com |
+----------+-----------+----------+------------+-------------------------------+
返回此行是因为NULL RegionId与正确的LanguageId匹配的优先级高于使用正确RegionId的NULL LanguageId匹配。
这是proc的代码(有效)。要真正回答我的问题,有没有更好的方法来写这个?如果我将来扩展这个表,我将继续增加UNION语句的数量,因此它不是真正可扩展的。
实际存储过程
CREATE PROC up_GetClientUrls
(
@ClientId INT
,@CountryId INT
,@RegionId INT
,@LanguageId INT
)
AS
BEGIN
SELECT TOP 1
prioritised.ClientId
,prioritised.CountryId
,prioritised.RegionId
,prioritised.LanguageId
,prioritised.URL
FROM
(
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,1 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId = @CountryId
AND c.RegionId = @RegionId
AND c.LanguageId = @LanguageId
UNION
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,2 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId IS NULL
AND c.RegionId = @RegionId
AND c.LanguageId = @LanguageId
UNION
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,3 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId = @CountryId
AND c.RegionId IS NULL
AND c.LanguageId = @LanguageId
UNION
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,4 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId IS NULL
AND c.RegionId IS NULL
AND c.LanguageId = @LanguageId
UNION
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,5 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId = @CountryId
AND c.RegionId = @RegionId
AND c.LanguageId IS NULL
UNION
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,6 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId IS NULL
AND c.RegionId = @RegionId
AND c.LanguageId IS NULL
UNION
SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,7 [priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND c.CountryId IS NULL
AND c.RegionId IS NULL
AND c.LanguageId IS NULL
) prioritised
ORDER BY prioritised.[Priority]
END
答案 0 :(得分:2)
这很容易(如果我理解正确的话)。你可以用很少的代码完成它。此外,如果需要,它很容易扩展。
这是一个工作示例
--Make a table
CREATE TABLE #ClientUrls (ClientId INT NOT NULL,CountryId INT NULL,RegionId INT NULL,LanguageId INT NULL,URL NVARCHAR(2048) NOT NULL)
--Put some data into it
INSERT INTO #ClientUrls (ClientId, CountryId, RegionId, LanguageId, URL)
VALUES
(1,1,1,1,'http://www.Website.com'),
(1,1,1,NULL,'http://www.Otherwebsite.com'),
(1,1,NULL,2,'http://www.Anotherwebsite.com')
--This would all be in your proc
----------------------------------------------
DECLARE @ClientId INT = 1
DECLARE @CountryId INT = 1
DECLARE @RegionId INT = 1
DECLARE @LanguageId INT = 2
--This is the interesting bit
----------------------------------------------
SELECT TOP 1 C.*
FROM #ClientUrls AS C
ORDER BY
--Order the ones with the best hit count near the top
IIF(ISNULL(C.ClientId, @ClientId) = @ClientId ,1,0) +
IIF(ISNULL(C.CountryId, @CountryId) = @CountryId ,2,0) +
IIF(ISNULL(C.RegionId, @RegionId) = @RegionId ,4,0) +
IIF(ISNULL(C.LanguageId,@LanguageId) = @LanguageId,8,0) DESC,
--Order the ones with the least nulls of each hit count near the top
IIF(C.ClientId IS NULL,0,1) +
IIF(C.CountryId IS NULL,0,2) +
IIF(C.RegionId IS NULL,0,4) +
IIF(C.LanguageId IS NULL,0,8) DESC
DROP TABLE #ClientUrls
多数民众赞成。在旧版本的SQL中,您无法使用IIF,但如果需要,可以将其替换为case语句。
就像这样。
每个匹配项都有一个值(有点像二进制数) 然后根据每个匹配项我们使用值,如果它不匹配则为0 通过累加总数,我们将始终选择最佳匹配组合。
value 1 2 4 8 Total value
+---------+----------+-----------+----------+------------+
| Ranking | ClientId | CountryId | RegionId | LanguageId |
+---------+----------+-----------+----------+------------+
| 1 | NOT NULL | NOT NULL | NOT NULL | NOT NULL | 15
| 2 | NOT NULL | NULL | NOT NULL | NOT NULL | 13
| 3 | NOT NULL | NOT NULL | NULL | NOT NULL | 11
| 4 | NOT NULL | NULL | NULL | NOT NULL | 9
| 5 | NOT NULL | NOT NULL | NOT NULL | NULL | 7
| 6 | NOT NULL | NULL | NOT NULL | NULL | 5
| 7 | NOT NULL | NULL | NULL | NULL | 1
+---------+----------+-----------+----------+------------+
我刚刚对此进行了更新,以确保您通过null选项获得非null版本。
如果您编辑结果以返回多于前1,您可以按正确的顺序查看项目。即如果你将语言从2改为1,你将得到1,1,1,1行超过1,1,1,Null选项
答案 1 :(得分:1)
未经测试,但你可以这样做:
SELECT TOP 1 c.ClientId,
c.CountryId,
c.RegionId,
c.LanguageId,
c.URL
FROM ClientUrls c
ORDER BY CASE
WHEN c.ClientId = @ClientId
THEN 1000
ELSE 0
END +
CASE
WHEN c.CountryId = @CountryId
THEN 200
WHEN c.CountryId IS NULL
THEN 100
ELSE 0
END +
CASE
WHEN c.RegionId = @RegionId
THEN 20
WHEN c.CountryId IS NULL
THEN 10
ELSE 0
END +
CASE
WHEN c.LanguageId = @LanguageId
THEN 2
WHEN c.CountryId IS NULL
THEN 1
ELSE 0
END DESC
通过为每个匹配项赋值并选择最高值,您可以减少所需的代码。但是你会增加所需的案例陈述数量,而不是工会数量。
这也可以是函数而不是存储过程。因此,在其他查询中可以更容易地使用它
答案 2 :(得分:0)
您可以将where子句更改为:
AND (c.CountryID = @CountryID OR c.CountryID IS NULL)
编码方式,它的代码更少。 但调整问题更多。
答案 3 :(得分:0)
另一种选择可能是尝试操纵NULL
值来创建层次结构,如下所示:
WITH priorities as (SELECT
c.ClientId
,c.CountryId
,c.RegionId
,c.LanguageId
,c.URL
,COALESCE(
NULLIF(c.CountryId,@CountryId),
NULLIF(c.RegionId,@RegionId),
NULLIF(c.LanguageId,@LanguageId),
1000000)
+ ISNULL(c.CountryId,200000)
+ ISNULL(c.RegionId,100000)
+ COALESCE(c.CountryId,RegionId,40000)
+ ISNULL(c.LanguageId,10000)
+ COALESCE(c.CountryId,c.LanguageId,4000)
+ COALESCE(c.CountryId,c.RegionId,c.LanguageId,1000)
[priority]
FROM ClientUrls c
WHERE c.ClientId = @ClientId
AND (c.CountryId = @CountryId
OR c.RegionId = @RegionId
OR c.LanguageId = @LanguageId)
)
SELECT TOP 1 ClientId,CountryId,RegionId,LanguageId,URL FROM priorities ORDER BY priority DESC