仅当计数大于一

时间:2016-04-28 11:30:18

标签: sql sql-server

我想知道如果不使用中间变量和条件子句,是否可以使用以下有点人为的例子。

考虑一个中间查询,它可以生成一个包含无行,一行或多行的结果集。大多数情况下它只产生一行,但是当多行时,应该将结果行连接到另一个表以将其修剪为一行或不行。如果有一行(而不是没有行),则需要返回原始中间查询生成的多个列。

我脑子里有类似跟随的东西,但它显然不起作用(在switch-case中没有多列,没有连接等),但也许它说明了这一点。我想要的是只返回SELECT@@ROWCOUNT = 1子句中的当前内容,如果它更大,请执行INNER JOINAuxilliary,将x修剪为一行或无行,然后返回。我不想多次搜索MainAuxilliary只有当x此处包含多行时才会{。}}。

SELECT x.MainId, x.Data1, x.Data2, x.Data3,
CASE         
     WHEN @@ROWCOUNT IS NOT NULL AND @@ROWCOUNT = 1 THEN
        1
     WHEN @@ROWCOUNT IS NOT NULL AND @@ROWCOUNT > 1 THEN
        -- Use here @id or MainId to join to Auxilliary and there 
        -- FilteringCondition = @filteringCondition to prune x to either 
        -- one or zero rows.
END         
FROM
(
    SELECT
        MainId,
        Data1,
        Data2,
        Data3
    FROM Main
    WHERE
        MainId = @id
) AS x;

CREATE TABLE Main
(
    -- This Id may introduce more than row, so it is joined to
    -- Auxilliary for further pruning with the given conditions.
    MainId INT,
    Data1 NVARCHAR(MAX) NOT NULL,
    Data2 NVARCHAR(MAX) NOT NULL,
    Data3 NVARCHAR(MAX) NOT NULL,

    AuxilliaryId INT NOT NULL
);


CREATE TABLE Auxilliary
(
    AuxilliaryId INT IDENTITY(1, 1) PRIMARY KEY,
    FilteringCondition NVARCHAR(1000) NOT NULL
);

在没有临时表变量和条件的情况下,这可以在一个查询中实现吗?不使用CTE?

一些样本数据将是

INSERT INTO Auxilliary(FilteringCondition)
VALUES
    (N'SomeFilteringCondition1'),
    (N'SomeFilteringCondition2'),
    (N'SomeFilteringCondition3');


INSERT INTO Main(MainId, Data1, Data2, Data3, AuxilliaryId)
VALUES
    (1, N'SomeMainData11', N'SomeMainData12', N'SomeMainData13', 1),
    (1, N'SomeMainData21', N'SomeMainData22', N'SomeMainData23', 2),
    (2, N'SomeMainData31', N'SomeMainData32', N'SomeMainData33', 3);

一个示例查询,实际上表现得像我一样,只要直接使用给定的ID查询Main,我就会想要进行连接。一个结果。

DECLARE @id AS INT = 1;
DECLARE @filteringCondition AS NVARCHAR(1000) = N'SomeFilteringCondition1';

SELECT *
FROM
    Main
    INNER JOIN Auxilliary AS aux ON aux.AuxilliaryId = Main.AuxilliaryId
WHERE MainId = @id AND aux.FilteringCondition = @filteringCondition;

2 个答案:

答案 0 :(得分:3)

您通常不使用 join 来减少左表的结果集。要限制结果集,您可以使用 where子句。结合另一个表格,这将是WHERE [NOT] EXISTS

所以让我们说这是你的主要问题:

select * from main where main.col1 = 1;

它返回以下结果之一:

  • 没有行,然后我们就完成了
  • 一行,然后我们也完成了
  • 多行,然后我们必须扩展where子句

带有扩展where子句的查询:

select * from main where main.col1 = 1
and exists (select * from other where other.col2 = main.col3);

返回以下结果之一:

  • 没有行,没关系
  • 一行,没关系
  • 多行 - 你说这是不可能的

因此,任务是一步完成。我计算记录并在其他表中为每条记录寻找匹配。然后......

  • 如果计数为零,我们无论如何都没有结果
  • 如果它是一个我拿那一行
  • 如果大于1,我会在另一个表中选择与其匹配的行,或者在没有匹配时为空

以下是完整查询:

select *
from
(
  select
    main.*,
    count(*) over () as cnt,
    case when exists (select * from other where other.col2 = main.col3) then 1 else 0 end
     as other_exists
  from main 
  where main.col1 = 1
) counted_and_checked
where cnt = 1 or other_exists = 1;

更新:我知道您希望避免对其他表进行不必要的访问。然而,这很难做到。

为了在必要时仅使用子查询,我们可以将其移到外面:

select *
from
(
  select
    main.*,
    count(*) over () as cnt
  from main 
  where main.col1 = 1
) counted_and_checked
where cnt = 1 or exists (select * from other where other.col2 = main.col3);

在我看来,这看起来好多了。但是OR左右两个表达式中没有优先权。因此,在评估cnt = 1之前,DBMS可能仍然在每条记录上执行子选择。

我知道使用从左到右的优先级的唯一操作,即一旦左侧的条件匹配就不会进一步查看COALESCE。所以我们可以做到以下几点:

select *
from
(
  select
    main.*,
    count(*) over () as cnt
  from main 
  where main.col1 = 1
) counted_and_checked
where coalesce( case when cnt = 1 then 1 else null end , 
                (select count(*) from other where other.col2 = main.col3) 
              ) > 0;

这可能看起来有点奇怪,但是当cnt为1时,应该阻止执行子查询。

答案 1 :(得分:1)

您可以尝试类似

的内容
select * from  Main m
where mainId=@id 
and @filteringCondition = case when(select count(*) from Main m2 where m2.mainId=@id) >1 
then (select filteringCondition from Auxilliary a where a.AuxilliaryId = m.AuxilliaryId) else @filteringCondition end

但它的查询速度不是很快。我最好使用临时表或if和两个查询。