删除具有复杂if的行

时间:2014-02-03 19:32:03

标签: sql sql-server

我想编写一个查询,它从表中删除重复项,其中Access列的值为1 - 5,但总是取最高数字,但如果它是5那么那应该是最低的,因为它们是如何设计数据库的。但是5没有访问权限。(在我看来,5应该是0。)

所以有一个ID列和一个Access列,如果有多个ID,则删除具有最低Access值的ID。但请记住将5视为0或最低。 enter image description here

所以我想的是:

Delete from [table]
Where [ID] > 1
AND [Access] = (CASE [Access] 
    WHEN Access = 1 THEN ____ <----'Do nothing')
    ...
    WHEN Access = 5 THEN ____ <----'Do Delete')

这就是我挣扎的地方。我如何检查ID,并查看哪个Access最高,如果存在则删除所有最低的Access。请记住,如果它是5然后4实际上更高,那么删除5。

太混乱了!

4 个答案:

答案 0 :(得分:2)

如果要删除首先拥有访问权限5并且其余部分已订购的每个ID的行,请执行以下操作:

with todelete as (
      select t.*, row_number() over (partition by id
                                     order by (case when access = 5 then -1 else access end)
                                    ) as seqnum
      from table t
     )
delete from todelete
    where seqnum = 1;

如果您只想在id

有多条记录时执行此操作
with todelete as (
      select t.*, row_number() over (partition by id
                                     order by (case when access = 5 then -1 else access end)
                                    ) as seqnum,
             count(*) over (partition by id) as cnt
      from table t
     )
delete from todelete
    where seqnum = 1 and cnt > 1;

编辑:

如果您想根据优先规则删除除一行以外的所有内容:

with todelete as (
      select t.*, row_number() over (partition by id
                                     order by (case when access = 5 then -1 else access end)
                                    ) as seqnum,
             count(*) over (partition by id) as cnt
      from table t
     )
delete from todelete
    where seqnum < cnt;

答案 1 :(得分:2)

如果将模5应用于Access列,您将获得以下转换:

1 % 5 = 1
2 % 5 = 2
3 % 5 = 3
4 % 5 = 4
5 % 5 = 0

正如您所看到的,5会产生最低的结果,其他则保持不变 - 似乎是您的案例的正确权限排名。考虑到这一点,我可能会尝试以下方法:

DELETE FROM u
FROM dbo.UserTable AS u
LEFT JOIN (
  SELECT ID, MAX(Access % 5) AS Access
  FROM dbo.UserTable
  GROUP BY ID
) AS keep
  ON u.ID = keep.ID
 AND u.Access % 5 = keep.Access
WHERE i.ID IS NULL
;

子查询返回一组ID,其中包含要保留的Access值。主查询将目标表反连接到该集合以确定要删除的行。

此方法在某种程度上特定于您的特定情况:在扩展Access的当前有效值集后,它可能无法正常工作。作为替代方案,您可以像其他人所建议的那样使用CASE,这肯定会更灵活。但是,我实际上建议您在AccessRank表中添加Access列,以指示哪个权限高于或低于其他权限。

这会使您的DELETE查询更复杂,但每次引入新的Access值时都不需要对其进行调整(您只需要在数据中定义正确的排名 ):

DELETE FROM u
FROM dbo.UserTable AS u
INNER JOIN dbo.Access AS a
  ON u.Access = a.ID
LEFT JOIN (
  SELECT u.ID, MAX(a.AccessRank) AS AccessRank
  FROM dbo.UserTable AS u
  INNER JOIN dbo.Access AS a
    ON u.Access = a.ID
) AS keep
  ON u.ID = keep.ID
 AND a.AccessRank = keep.AccessRank
;

暗示AccessRank仅包含唯一排名。

答案 2 :(得分:1)

示例访问表:

|id|access|
-----------
|1 |  1   |
|1 |  2   |
|1 |  5   |
|2 |  1   |
|2 |  3   | 

运行以下查询:

DELETE a 
FROM acc a
INNER JOIN 
(SELECT row= ROW_NUMBER() OVER(PARTITION BY ID ORDER BY  (CASE WHEN ACCESS=5 THEN 0 ELSE ACCESS  END) DESC), * FROM acc) b
ON a.id = b.id AND a.access = b.access 
WHERE b.row > 1

结果是:

|id|access|
|1 |  2   |
|2 |  3   | 

答案 3 :(得分:-1)

这应该是最快且最便携的解决方案,因为它允许在Access上使用索引并使用标准SQL。

SQL Fiddle

MS SQL Server 2008架构设置

CREATE TABLE Table1
    ([ID] int, [Access] int)
;

INSERT INTO Table1
    ([ID], [Access])
VALUES
    (1, 5),
    (1, 1),
    (1, 2),
    (2, 5),
    (2, 1),
    (2, 2),
    (3, 5)
;

查询1

delete t
from Table1 t
inner join (
    select ID, max(Access) as MaxAccess
    from Table1
    where Access <> 5
    group by ID
) pm on t.Access <> pm.MaxAccess
    and t.ID = pm.ID

select * from Table1

<强> Results

| ID | ACCESS |
|----|--------|
|  1 |      2 |
|  2 |      2 |
|  3 |      5 |