按可空列分组的特殊方式

时间:2018-09-24 20:55:08

标签: sql sql-server group-by

我想创建一个唯一的索引视图,这样,如果将任何两个非空Location分配给同一个Id,那么它将违反索引并且不允许它们。

我像这样开始我的观点

-- This is what I wish I could do but its wrong
SELECT Id, Location, COUNT(*) as Count, COUNT(Location) as LocationCount
FROM #X
GROUP BY Id, Location
ORDER BY Id, Location 

这不起作用,因为存在具有单个ID的位置具有NULL位置和非null位置的位置,这些位置根据业务规则是有效的,但根据分组重复了该ID。

这表明:

DROP TABLE IF EXISTS #X 

SELECT *
INTO #X
FROM (
    SELECT 1 as Id, 1 as Location
    UNION
    Select 1 as Id, NULL as Location
    UNION
    Select 2 as Id, NULL as Location
    UNION
    Select 3 as Id, 2 as Location
) X

-- This is what I wish I could do but its wrong
SELECT Id, Location, COUNT(*) as Count, COUNT(Location) as LocationCount
FROM #X
GROUP BY Id, Location
ORDER BY Id, Location

结果:

enter image description here

所需:

enter image description here


由于ID是重复的,因此无法将其作为索引视图。

我想要的只是使用AVG或MAX或忽略NULL的东西即可完成:

SELECT Id, AVG(Location) as Location, COUNT(*) as Count, COUNT(Location) as LocationCount
FROM #X
GROUP BY Id
ORDER BY Id, Location

但这不起作用,因为在索引视图中不允许AVG,而且还因为它仅在Id上分组,因此,如果将两个非null分配给单个ID,例如6和8,它将返回两者的平均值(7)而不是不允许它。

enter image description here


因此我想到了一个想法,我可以制作两个唯一的视图,一个用于null,一个用于非null,然后创建第三个视图,根据我想要的逻辑将它们组合在一起。这项工作可行,但是似乎可以完成我想要的事情,并且在使用视图时需要更多的开销和计算/处理,但是比没有索引视图要好:

;WITH IndexableViewNonNulls AS (
    SELECT Id, Location, COUNT(*) as Count
    FROM #X
    WHERE Location IS NOT NULL
    GROUP BY Id, Location
), IndexableViewNulls AS (
    SELECT Id, Location, COUNT(*) as Count
    FROM #X
    WHERE Location IS NULL
    GROUP BY Id, Location
), CTE AS (
    SELECT *, Count as LocationCount
    FROM IndexableViewNonNulls
    UNION ALL
    SELECT *, 0 as LocationCount
    FROM IndexableViewNulls
)

SELECT 
      Id
      ,AVG(Location) as Location
      ,SUM(Count) as Count
      ,SUM(LocationCount) as LocationCount
FROM CTE
GROUP BY Id

enter image description here

这是完整的小提琴:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=f3df7c4b5703e9270ea1efe9bef5c152

编辑:这是另一个带有索引视图的小提琴。请注意,插入NULL会违反唯一索引,但是我不希望这样做。我只希望在所有两个非null的同时保持视图不变的情况下违反该规则(如果ALL为null,则位置显示NULL,如果存在1个或多个位置,则位置显示

https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=544b5983bf25cd9c2ff1f55d68bd34d8

编辑2:这是我的解决方案,使用2个索引视图和1个普通视图将它们放在一起:https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b8cf5ff638305a0d228c856b6391246d

编辑3:#X表不是普通表,仅在表上添加索引不是有效的解决方案。在实际的场景中,表#X本身就是视图,因此解决方案必须使用索引视图

1 个答案:

答案 0 :(得分:0)

我认为您可以创建过滤索引:

create unique index unq_index_x_id_location on x(id, location) where location is not null;

如果您只想满足此约束的视图,那么我认为这可以满足您的要求:

SELECT Id, MIN(Location) as location
FROM #X
GROUP BY Id
HAVING MIN(Location) = MAX(Location) OR MIN(LOCATION) IS NULL;