尝试为复杂的搜索方案构建SQL语句

时间:2009-01-16 22:04:44

标签: sql sql-server-2005 tsql search

我正在尝试为以下搜索方案构建SQL语句:

我试图根据表B中状态列的值返回表A的单个记录的所有列。表A中的每个记录在表B中可以有多行,使其成为一对多关系。 status列可以为空,数据类型为整数。

以下是表B中状态的可能值:

  • NULL =待定,
  • 1 =已批准,
  • 2 =拒绝,
  • 6 =强制批准,
  • 7 =强制拒绝

最终用户可以搜索以下场景:

  • 已批准 - 所有表B记录的状态值必须为1或6。
  • 拒绝 - 一个表B记录必须具有值2或5.任何其他记录可以具有1,6或null。
  • 待定 - 所有表B记录的值均为1,6或null。一条记录必须为空,因为它不被视为已完成。

更新
我咨询了我们的一位DBA,他开发了以下解决方案:

批准:

SELECT a.* FROM TableA a INNER JOIN TableB ON b.id = a.id
WHERE
(b.status in (1,6) and b.status IS NOT NULL) AND
b.id NOT IN (SELECT id from TableB  WHERE status IS NULL)
AND b.id NOT IN (SELECT id from TableB WHERE status in (2,7))

拒绝:

SELECT a.* FROM TableA a INNER JOIN TableB ON b.id = a.id
WHERE
(b.status in (2,7))

未决:

SELECT a.* FROM TableA a INNER JOIN TableB ON b.id = a.id
WHERE
(b.status IN (1,6) OR b.status IS NULL)
AND b.id NOT IN (SELECT b.id FROM TableA a INNER JOIN TableB b ON b.id = a.id WHERE (b.status IN (1,6) AND b.status IS NOT NULL) AND b.id NOT IN (SELECT id from TableB WHERE status IS NULL))
AND b.id NOT IN (SELECT id FROM TableB WHERE status IN (2,7))

更新2:
@Micth Wheat - 如何使用EXIST / NOT EXIST t-sql关键字重构以下解决方案?

5 个答案:

答案 0 :(得分:2)

作为'已批准'的示例:

select 
    * 
from 
    A 
where
    (select count(*) from B where B.parent_id = A.id and B.status in (1,6)) > 0
and (select count(*) from B where B.parent_id = A.id and B.status not in (1,6)) = 0

重构使用 exists 不存在

select 
    * 
from 
    A 
where
    exists (select * from B where B.parent_id = A.id and B.status in (1,6)) 
and not exists (select * from B where B.parent_id = A.id and B.status not in (1,6)) 

如果你已经通过了一个标准,你可以将它全部打包在这样的一个查询中,如果它更方便的话:

select 
    * 
from 
    A 
where     
    (@Criteria = 'Approved'
and (select count(*) from B where B.parent_id = A.id and B.status in (1,6)) > 0
and (select count(*) from B where B.parent_id = A.id and B.status not in (1,6)) = 0
    )
or  (@Criteria = 'Denied'
and (select count(*) from B where B.parent_id = A.id and B.status in (2,7)) > 0
    )
or  (@Criteria = 'Pending'
and (select count(*) from B where B.parent_id = A.id and B.status not in (2,7)) = 0
and (select count(*) from B where B.parent_id = A.id and B.status is null) > 0
    )

注意,我根据您的示例数据将Denied示例更改为值2和7,而不是2和5。

编辑:您也可以使用存在不存在,正如Joe所说。

编辑:使用max(case ...)的方法,通常也被视为sum(case ...)来计算值,在某些情况下确实表现更好(主要取决于你的数据量是否性能增加是引人注目 - 有时它可能是一个很大的区别)。我个人觉得子查询更具可读性,所以我从它们开始,如果需要更好的性能,我会对两种方法进行基准测试,如果max(case ...)效果更好,我会切换。

答案 1 :(得分:1)

从我读到的Chris Teixeira和hova使用基本相同的逻辑,但是;
- 霍瓦只对表格进行一次解析 - Chris Teixeira多次解析表格 => Hova的技术是首选(在我看来)

然而,Hova做得非常错误......

逻辑应该是:

- If Any 2 or 7 records   => DENIED
- ElseIf Any NULL records => PENDING
- Else                    => ACCEPTED

这给出了以下代码......

SELECT
    [main].id,
    CASE WHEN MAX(CASE WHEN [status].value IN (2,7) THEN 1 ELSE 0 END) = 1 THEN 'DENIED'
         WHEN MAX(CASE WHEN [status].value IS NULL  THEN 1 ELSE 0 END) = 1 THEN 'PENDING'
         ELSE 'ACCEPTED' END
FROM
    [main]
INNER JOIN
    [status]
        ON [main].id = [status].main_id
GROUP BY
    [main].id

此外,使用MAX而不是SUM(Hova使用)意味着查询引擎只需要找到一个匹配,而不是几个匹配。此外,优化器更容易在[status]表上使用适当的索引。 (在这种情况下,索引在[status]表上按顺序为(main_id,value)。

民主党。

编辑:

类似的东西可能如下。我没有在这里测试的SQL实例,所以我不能告诉你它是否更快,但我想它可能是......

SELECT
    [main].id,
    MIN(CASE WHEN [status].value IN (2,7) THEN -1        -- Denied
             WHEN [status].value IS NULL  THEN  0        -- Pending
             ELSE                               1  END)  -- Accepted
FROM
    [main]
INNER JOIN
    [status]
        ON [main].id = [status].main_id
GROUP BY
    [main].id

编辑:

另一个选择是加入映射表而不是使用CASE语句。

DECLARE @map TABLE (
   status_value   INT,
   status_result  INT
   )

INSERT INTO @map VALUES (1,    1)
INSERT INTO @map VALUES (2,   -1)
INSERT INTO @map VALUES (6,    1)
INSERT INTO @map VALUES (7,   -1)
INSERT INTO @map VALUES (NULL, 0)

SELECT
    [main].id,
    MIN([map].status_result)
FROM
    [main]
INNER JOIN
    [status]
        ON [main].id = [status].main_id
INNER JOIN
    @map AS [map]
        ON [status].value = [map].status_value
        OR ([status].value IS NULL AND [map].status_value IS NULL)
        -- # This has been faster than using ISNULLs in my experience...
GROUP BY
    [main].id

答案 2 :(得分:0)

您希望使用一些存在语句。

对于所有值必须为某些值的内容,您可以针对其他可能的值添加

- 核准

exists(select 1 from B where A.id = B.id and status in (1,6))
and not exists(select 1 from B where A.id = B.id and (status is null or status not in (1,6)))

答案 3 :(得分:0)

也许是这样的

select CASE WHEN SUM(CASE WHEN ISNULL(b.status, 0) IN (1,6) THEN 1 ELSE 0 END) = COUNT(*) THEN 'Approved'
            WHEN SUM(CASE WHEN ISNULL(b.status, 0) IN (2,5) THEN 1 ELSE 0 END) > 0 THEN 'Denied'
            WHEN SUM(CASE WHEN ISNULL(b.status, 0) IN (1,0,6) THEN 1 ELSE 0 END) = COUNT(*)
                AND SUM(CASE WHEN b.status IS NULL THEN 1 else 0 END) > 0  THEN 'Pending'
            ELSE '???' END as Status, <a column list>
FROM A 
INNER JOIN b ON a.id = b.id
group by <a column list>

答案 4 :(得分:0)

您还询问了如何使用EXISTS来解决查询问题。我仍然没有任何东西可以测试这个,但我会尝试类似以下内容...

批准:

SELECT
    a.*
FROM
    TableA a
WHERE
    NOT EXISTS (SELECT * FROM TableB b WHERE b.id = a.id AND b.status IN (NULL,1,6))

拒绝:

SELECT
    a.*
FROM
    TableA a
WHERE
    EXISTS (SELECT * FROM TableB b WHERE b.id = a.id AND b.status IN (1,6))

未决:

SELECT
    a.*
FROM
    TableA a
WHERE
    EXISTS (SELECT * FROM TableB b WHERE b.id = a.id AND b.status IS NULL)
    AND NOT EXISTS (SELECT * FROM TableB b WHERE b.id = a.id AND b.status IN (1,6))