[英]SQL - “NOT IN” in WHERE clause using INNER JOIN not working
我需要过滤基于子表数据的表。
我将以催眠数据为例,以便于解释:
Cars
Attributes
(例如Color
, car type
, accessories
) 这些属性具有一个id( idOption
)和所选值( idList
)
因此,在一个示例中,我需要过滤所有颜色为( idOption = 10
)黄色( idList = 45
)的汽车。 我无法直接对此进行过滤,因为搜索需要考虑其他选项的结果(包括类型,附件)。
当我仅将NOT IN
用于一张桌子时,它可以工作。 但是,当我使用INNER JOIN
合并这两个表时,它不起作用。
因此,总而言之,我需要使用给定值过滤3 idOption(当不为NULL时),这需要反映在主表中(按产品分组)。
台式车 :
idProduct | Description
1 Product A
2 Product B
3 Product C
表属性 :
idRow idProduct idOption idList
---------------------------------------
1 1 10 45
2 2 10 46
3 3 10 47
4 1 11 10
5 2 11 98
6 1 14 56
7 3 16 28
8 2 20 55
这是我创建的不起作用的存储过程:
ALTER PROCEDURE [dbo].[SP_GET_TestSearch]
(@Param1 BIGINT = NULL,
@PValue1 BIGINT = NULL,
@Param2 BIGINT = NULL,
@PValue2 BIGINT = NULL,
@Param3 BIGINT = NULL,
@PValue3 BIGINT = NULL)
AS
SET NOCOUNT ON;
SELECT
Cars.idProduct,
Cars.[Description]
FROM
Cars
INNER JOIN
Attributes ON Cars.idProduct = Attributes.idProduct
WHERE
((@Param1 IS NULL OR (idOption NOT IN (@Param1)))
AND
(@Param2 IS NULL OR (idOption NOT IN (@Param2)))
AND
(@Param3 IS NULL OR (idOption NOT IN (@Param3))))
OR
(idOption = ISNULL(@Param1, NULL)
AND idList = ISNULL(@PValue1, NULL))
OR
(idOption = ISNULL(@Param2, NULL)
AND idList = ISNULL(@PValue2, NULL))
OR
(idOption = ISNULL(@Param3, NULL)
AND idList = ISNULL(@PValue3, NULL))
GROUP BY
Cars.idProduct, Cars.[Description]
这里有几件事。
首先,出于各种原因,这种捕获所有程序有点反模式,请参见此处以获取完整说明:-https: //sqlinthewild.co.za/index.php/2018/03/13/revisiting -捕获所有查询/
其次,您需要非常小心,不要将NOT IN与列表中的可为空值一起使用: http : //www.sqlbadpractices.com/using-not-in-operator-with-null-values/
我为表添加了DDL:-
IF OBJECT_ID('Attributes') IS NOT NULL
DROP TABLE Attributes;
IF OBJECT_ID('Cars') IS NOT NULL
DROP TABLE Cars;
IF OBJECT_ID('SP_GET_TestSearch') IS NOT NULL
DROP PROCEDURE SP_GET_TestSearch
CREATE TABLE Cars
(idProduct INT PRIMARY KEY
, Description VARCHAR(20) NOT NULL);
CREATE TABLE Attributes
(idRow INT PRIMARY KEY
, idProduct INT NOT NULL FOREIGN KEY REFERENCES dbo.Cars(idProduct)
, idOption INT NOT NULL
, idList INT NOT NULL);
INSERT INTO dbo.Cars
VALUES
(1, 'Product A')
,(2 , 'Product B')
,(3, 'Product C');
INSERT INTO dbo.Attributes
(
idRow,
idProduct,
idOption,
idList
)
VALUES (1,1,10,45)
,(2,2,10,46)
,(3,3,10,47)
,(4,1,11,10)
,(5,2,11,98)
,(6,1,14,56)
,(7,3,16,28)
,(8,2,20,55);
GO
查询的问题是,对于您未指定的任何idOption,块的第一部分始终被评估为TRUE:
((@Param1 IS NULL OR (idOption NOT IN (@Param1)))
AND
(@Param2 IS NULL OR (idOption NOT IN (@Param2)))
AND
(@Param3 IS NULL OR (idOption NOT IN (@Param3))))
解释; 如果我通过以下内容:
DECLARE @Param1 BIGINT
, @Param2 BIGINT
, @Param3 BIGINT
, @PValue1 BIGINT
, @PValue2 BIGINT
, @PValue3 BIGINT;
SET @Param1 = 11
SET @Pvalue1 = 42
SET @Param2 = 11
SET @Pvalue2 = 10
SET @Param3 = 14
SET @PValue3= 56
EXEC dbo.SP_GET_TestSearch @Param1, @PValue1, @Param2, @PValue2, @Param3, @PValue3
然后,您实际上将WHERE idOption NOT IN (11,14)
作为该子句第一部分的评估,因此将返回所有其他行。
我怀疑您真的希望WHERE子句是:
WHERE
(@Param1 IS NULL AND @Param2 IS NULL AND @Param3 IS NULL)
OR
(idOption = @Param1
AND idList = @PValue1)
OR
(idOption = @Param2
AND idList = @PValue2)
OR
(idOption = @Param3
AND idList = @PValue3)
以下代码演示了如果车辆具有任何 “不良”属性值,则如何实现将车辆从查询结果中排除的逻辑。 拒绝由... where not exists ...
)处理... where not exists ...
用于检查每辆车的“不良”属性值。
而不是使用各种(希望的)配对参数来传递不希望的属性,而是在表中传递值。 用于实现此目的的存储过程应使用表值参数 (TVP)来传递表。
-- Sample data.
declare @Cars as Table ( CarId Int Identity, Description VarChar(16) );
insert into @Cars ( Description ) values
( 'Esplanade' ), ( 'Tankigator' ), ( 'Land Yacht' );
select * from @Cars;
declare @Properties as Table ( PropertyId Int Identity, Description VarChar(16) );
insert into @Properties ( Description ) values
( 'Turbochargers' ), ( 'Superchargers' ), ( 'Hyperchargers' ), ( 'Color' ), ( 'Spare Tires' );
select * from @Properties;
declare @CarProperties as Table ( CarId Int, PropertyId Int, PropertyValue Int );
insert into @CarProperties ( CarId, PropertyId, PropertyValue ) values
( 1, 1, 1 ), ( 1, 4, 24 ), ( 1, 4, 42 ), -- Two tone!
( 2, 2, 1 ), ( 2, 4, 7 ),
( 3, 1, 2 ), ( 3, 4, 0 ), ( 3, 5, 6 );
select C.CarId, C.Description as CarDescription,
P.PropertyId, P.Description as PropertyDescription,
CP.PropertyValue
from @Cars as C inner join
@CarProperties as CP on CP.CarId = C.CarId inner join
@Properties as P on P.PropertyId = CP.PropertyId
order by C.CarId, P.PropertyId;
-- Test data: Avoid vehicles that have _any_ of these property values.
-- This should be passed to the stored procedure as a table-value parameter (TVP).
declare @BadProperties as Table ( PropertyId Int, PropertyValue Int );
insert into @BadProperties ( PropertyId, PropertyValue ) values
( 2, 1 ), ( 2, 2 ), ( 2, 4 ),
( 4, 62 ), ( 4, 666 );
select BP.PropertyId, BP.PropertyValue, P.Description
from @BadProperties as BP inner join
@Properties as P on P.PropertyId = BP.PropertyId;
-- Query the data.
select C.CarId, C.Description as CarDescription
from @Cars as C
where not exists (
select 42
from @CarProperties as CP inner join
@BadProperties as BP on BP.PropertyId = CP.PropertyId and BP.PropertyValue = CP.PropertyValue
where CP.CarId = C.CarId )
order by C.CarId;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.