问题:
我想根据Exception表中当前分配的优先级,返回主数据表中的所有行以及最高优先级异常。
我在下面创建了一个简化的数据设置示例(使用创建脚本),希望您可以帮助解决相当快速的T-SQL问题。
设置:
我有一个主数据表,其中每行可以有一个或多个异常存储为位掩码。
CREATE TABLE [dbo].[PrimaryData](
Id [INT] IDENTITY(1,1) NOT NULL,
SomeData [VARCHAR](30) NOT NULL,
Exceptions [INT] NOT NULL,
)
INSERT INTO [dbo].[PrimaryData](SomeData, Exceptions)
VALUES('Data A', 0)
INSERT INTO [dbo].[PrimaryData](SomeData, Exceptions)
VALUES('Data B', 6)
INSERT INTO [dbo].[PrimaryData](SomeData, Exceptions)
VALUES('Data C', 6)
INSERT INTO [dbo].[PrimaryData](SomeData, Exceptions)
VALUES('Data D', 192)
INSERT INTO [dbo].[PrimaryData](SomeData, Exceptions)
VALUES('Data E', 132)
异常存储在查找表中纯粹是因为每个异常都被赋予了用户指定的优先级。此表不能由最终用户添加或删除行,他们只能控制每个例外的优先级, 1是最高。
CREATE TABLE [dbo].[Exception](
Id [INT] IDENTITY(1,1) NOT NULL,
Priority [INT] NOT NULL,
Mask [SMALLINT] NOT NULL,
Description [VARCHAR](30) NOT NULL
)
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(1, 1, 'Exception A')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(2, 2, 'Exception B')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(3, 4, 'Exception C')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(4, 8, 'Exception D')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(5, 16, 'Exception E')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(6, 32, 'Exception F')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(7, 64, 'Exception G')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(8, 128, 'Exception H')
INSERT INTO [dbo].[Exception] (Priority, Mask, Description)
VALUES(9, 256, 'Exception I')
因此,基于提供的示例数据,我想返回SomeData,Mask(具有最高优先级)和Description(具有最高优先级)。
即。
| Data B | 2 | Exception B
显然,我需要以最有效的方式执行此操作,因为主数据表中可能会返回25K行。
提前致谢。
答案 0 :(得分:2)
SELECT *
FROM PrimaryData pd
CROSS APPLY
(
SELECT TOP 1 *
FROM Exception e
WHERE e.Mask & pd.Exceptions <> 0
ORDER BY
e.Priority
) q
答案 1 :(得分:1)
这将获得您想要的单个PrimaryData行。
select top 1 SomeData, Mask
from PrimaryData
inner join Exceptions
on (PrimaryData.Exceptions & Exceptions.Mask <> 0)
where PrimaryData.Id = 27
order by Priority
对于所有行,这样的东西应该有效(按照Quassnoi的建议编辑)
with data as (
select SomeData, Mask, row_number() over
(partition by PrimaryData.Id order by Priority) AS row
from PrimaryData
inner join Exceptions
on (PrimaryData.Exceptions & Exceptions.Mask <> 0)
)
select *
from data
where row = 1
编辑改变|到&amp;
答案 2 :(得分:0)
首先创建一个检测异常掩码的(确定性)函数。这个位于最低位:
CREATE FUNCTION GetPriorityMask(@value int)
RETURNS smallint
with schemabinding
AS
BEGIN
declare @mask smallint
if @value = 0 return null
set @mask = 1
while @mask <= @value
begin
if @value | @mask = @value
break;
set @mask = @mask * 2
end
RETURN @mask
END
然后将一个持久计算列添加到PrimaryDataTable,该值是函数的结果。
alter table PrimaryData
add PriorityMask as (dbo.GetPriorityMask(Exceptions)) persisted
现在你只需要为列添加一个索引
create index IX_PrimaryDate_PriorityMask
on PrimaryData(PriorityMask)
当然,添加外键也不是一个坏主意,但首先应该在异常表中添加一个唯一的密钥:
alter table Exception
add constraint UQ_Exception_Mask UNIQUE (Mask)
现在添加外键
alter table PrimaryData
add constraint FK_PrimaryData_Exception foreign key(PriorityMask) references Exception(Mask)
现在检索您的数据:
select *
from PrimaryData
left join Exception on PrimaryData.PriorityMask = Exception.Mask
答案 3 :(得分:0)
我的答案的灵感来自“查找整数对数基数10的整数,显而易见的方法”技巧here。
请注意,如果您要安排优先级以使最高位对应于最高优先级异常,则可以直接使用该技巧,适用于基数2.或者,您可以加入简单公式FLOOR(LOG(Exceptions)/LOG(2)
。 (在这两种解决方案中,您需要允许掩码的最高位使用或特殊情况,因为它会使整数为负。)
SELECT SomeData, Mask, Description
FROM PrimaryData
INNER JOIN Exception ON
CASE
WHEN Exceptions & 0x1 <> 0 THEN 1
WHEN Exceptions & 0x2 <> 0 THEN 2
WHEN Exceptions & 0x4 <> 0 THEN 4
WHEN Exceptions & 0x8 <> 0 THEN 8
WHEN Exceptions & 0x10 <> 0 THEN 16
WHEN Exceptions & 0x20 <> 0 THEN 32
WHEN Exceptions & 0x40 <> 0 THEN 64
WHEN Exceptions & 0x80 <> 0 THEN 128
WHEN Exceptions & 0x100 <> 0 THEN 256
WHEN Exceptions & 0x200 <> 0 THEN 512
WHEN Exceptions & 0x400 <> 0 THEN 1024
WHEN Exceptions & 0x800 <> 0 THEN 2048
WHEN Exceptions & 0x1000 <> 0 THEN 4096
WHEN Exceptions & 0x2000 <> 0 THEN 8192
WHEN Exceptions & 0x4000 <> 0 THEN 16384
WHEN Exceptions & 0x8000 <> 0 THEN 32768
WHEN Exceptions & 0x10000 <> 0 THEN 65536
WHEN Exceptions & 0x20000 <> 0 THEN 131072
WHEN Exceptions & 0x40000 <> 0 THEN 262144
WHEN Exceptions & 0x80000 <> 0 THEN 524288
WHEN Exceptions & 0x100000 <> 0 THEN 1048576
WHEN Exceptions & 0x200000 <> 0 THEN 2097152
WHEN Exceptions & 0x400000 <> 0 THEN 4194304
WHEN Exceptions & 0x800000 <> 0 THEN 8388608
WHEN Exceptions & 0x1000000 <> 0 THEN 16777216
WHEN Exceptions & 0x2000000 <> 0 THEN 33554432
WHEN Exceptions & 0x4000000 <> 0 THEN 67108864
WHEN Exceptions & 0x8000000 <> 0 THEN 134217728
WHEN Exceptions & 0x10000000 <> 0 THEN 268435456
WHEN Exceptions & 0x20000000 <> 0 THEN 536870912
WHEN Exceptions & 0x40000000 <> 0 THEN 1073741824
WHEN Exceptions & 0x80000000 <> 0 THEN -2147483648
ELSE 0
END = Mask
WHERE Exceptions <> 0
ORDER BY PrimaryData.Id