[英]Doing a join only if count is greater than one
I wonder if the following a bit contrived example is possible without using intermediary variables and a conditional clause. 我不知道下面的一些人为设计的示例是否可以在不使用中间变量和条件子句的情况下实现。
Consider an intermediary query which can produce a result set that contain either no rows, one row or multiple rows. 考虑一个中间查询,该查询可以生成不包含任何行,一行或多行的结果集。 Most of the time it produces just one row, but when multiple rows, one should join the resulting rows to another table to prune it down to either one or no rows. 在大多数情况下,它只产生一行,但是当产生多行时,应该将结果行连接到另一张表,以将其删减为一行或不行。 After this if there is one row (as opposed to no rows), one would want to return multiple columns as produced by the original intermediary query. 在此之后,如果只有一行(而不是没有行),则需要返回原始中间查询产生的多列。
I have in my mind something like following, but it won't obviously work (multiple columns in switch-case, no join etc.), but maybe it illustrates the point. 我在脑海中有以下类似的想法,但是它显然不起作用(切换情况下有多个列,没有联接等),但是也许可以说明这一点。 What I would like to have is to just return what is currently in the SELECT
clause in case @@ROWCOUNT = 1
or in case it is greater, do a INNER JOIN
to Auxilliary
, which prunes down x
to either one row or no rows and then return that. 我想有是只返回当前什么是在SELECT
子句中的情况下, @@ROWCOUNT = 1
或如果它是更大的,做一个INNER JOIN
到Auxilliary
,其修剪下来x
到一行或无行然后返回。 I don't want to search Main
more than once and Auxilliary
only when x
here contains more than one row. 我不想仅在x
包含多个行的情况下搜索Main
和Auxilliary
。
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
);
Would this be possible in one query without a temporary table variable and a conditional? 如果没有一个临时表变量和一个条件语句,是否可以在一个查询中实现? Without using a CTE? 不使用CTE?
Some sample data would be 一些样本数据将是
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);
And a sample query, which actually behaves as I'd like it to behave with the caveat I'd want to do the join only if querying Main
directly with the given ID produces more than one result. 还有一个示例查询,它的行为实际上与我希望的行为类似,仅当使用给定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;
You don't usually use a join to reduce the result set of the left table. 您通常不使用联接来减少左表的结果集。 To limit a result set you'd use the where clause instead. 为了限制结果集,您可以使用where子句 。 In combination with another table this would be WHERE [NOT] EXISTS
. 结合另一个表,这将是WHERE [NOT] EXISTS
。
So let's say this is your main query: 因此,假设这是您的主要查询:
select * from main where main.col1 = 1;
It returns one of the following results: 它返回以下结果之一:
The query with the extended where clause: 带有扩展where子句的查询:
select * from main where main.col1 = 1
and exists (select * from other where other.col2 = main.col3);
which returns one of the following results: 返回以下结果之一:
So the task is to do this in one step instead. 因此,任务是一步执行此操作。 I count records and look for a match in the other table for every record. 我对记录进行计数,并在每张记录的另一个表中查找匹配项。 Then ... 然后 ...
Here is the full query: 这是完整的查询:
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;
UPDATE: I understand that you want to avoid unnecessary access to the other table. 更新:我知道您要避免不必要地访问另一个表。 This is rather difficult to do however. 但是,这很难做到。
In order to only use the subquery when necessary, we could move it outside: 为了仅在必要时使用子查询,我们可以将其移到外部:
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);
This looks much better in my opinion. 我认为这看起来要好得多。 However there is no precedence among the two expressions left and right of an OR
. 但是,在OR
左右两个表达式之间没有优先级。 So the DBMS may still execute the subselect on every record before evaluating cnt = 1
. 因此,DBMS 仍可以在评估cnt = 1
之前对每个记录执行子选择。
The only operation that I know of using left to right precedence, ie doesn't look further once a condition on the left hand side is matched is COALESCE
. 我知道的使用左到右优先级的唯一操作(即,一旦匹配左侧条件就不再看)是COALESCE
。 So we could do the following: 因此,我们可以执行以下操作:
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;
This may look a bit strange, but should prevent the subquery from being executed, when cnt is 1. 这可能看起来有些奇怪,但是当cnt为1时,应阻止执行子查询。
You may try something like 您可以尝试类似
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
but it's hardly very fast query. 但这并不是非常快速的查询。 I'd better use temp table or just if
and two queries. 我最好使用临时表或只是if
和两个查询。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.