我想知道如果不使用中间变量和条件子句,是否可以使用以下有点人为的例子。
考虑一个中间查询,它可以生成一个包含无行,一行或多行的结果集。大多数情况下它只产生一行,但是当多行时,应该将结果行连接到另一个表以将其修剪为一行或不行。如果有一行(而不是没有行),则需要返回原始中间查询生成的多个列。
我脑子里有类似跟随的东西,但它显然不起作用(在switch-case中没有多列,没有连接等),但也许它说明了这一点。我想要的是只返回SELECT
中@@ROWCOUNT = 1
子句中的当前内容,如果它更大,请执行INNER JOIN
到Auxilliary
,将x
修剪为一行或无行,然后返回。我不想多次搜索Main
,Auxilliary
只有当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;
答案 0 :(得分:3)
您通常不使用 join 来减少左表的结果集。要限制结果集,您可以使用 where子句。结合另一个表格,这将是WHERE [NOT] EXISTS
。
所以让我们说这是你的主要问题:
select * from main where main.col1 = 1;
它返回以下结果之一:
带有扩展where子句的查询:
select * from main where main.col1 = 1
and exists (select * from other where other.col2 = main.col3);
返回以下结果之一:
因此,任务是一步完成。我计算记录并在其他表中为每条记录寻找匹配。然后......
以下是完整查询:
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
和两个查询。