复杂的SQL选择查询

时间:2013-10-04 06:03:26

标签: sql sql-server tsql stored-procedures if-statement

我有一个存储过程需要不同的if条件才能正常工作。

该过程有2个参数,即@CategoryID和@ClassID,它们基本上来自UI树视图功能。 @CategoryID对应于父节点,而@ClassID对应于子节点。

基于以上参数,我需要从具有CategoryID和ClassID作为列的表中进行选择(列代码)。

现在有两种情况:

情景1

@CategoryID:A

@ClassID:B(它是CategoryID A的子节点)

所需结果:代码仅对应于ClassID B,基本上是交集

情景2

@CategoryID:A

@ClassID:C(不是CategoryID A的子节点)

需要的结果:对应于CategoryID A的代码,以及ClassID B,基本上是一个联合

我写的程序给了我第二个场景的正确答案,但第一个场景失败了。以下是我的程序:

ALTER PROCEDURE [dbo].[uspGetCodes]
@CategoryID varchar(50),
@ClassID varchar(50)
AS
BEGIN
BEGIN TRY
DECLARE @SQLQuery NVARCHAR(MAX)

SET @SQLQuery=N'SELECT Code FROM dbo.ClassToCategoryMapping WHERE '
IF (@CategoryID IS NULL OR @CategoryID='')
    BEGIN
    SET @SQLQuery=@SQLQuery + 'ClassId IN ('+@ClassID+')'
    PRINT(@SQLQuery)
    EXEC(@SQLQuery)
    END

ELSE IF (@ClassID IS NULL OR @ClassID='')
    BEGIN
    SET @SQLQuery=@SQLQuery+'CategoryID IN ('+@CategoryID+')'
    PRINT(@SQLQuery)
    EXEC(@SQLQuery)
    END

ELSE 
    BEGIN
    SET @SQLQuery=@SQLQuery+'(CategoryID IN ('+@CategoryID+') OR ClassId IN ('+@ClassID+') )'
    PRINT(@SQLQuery)
    EXEC(@SQLQuery)
    END
END TRY

BEGIN CATCH
SELECT ERROR_NUMBER() AS 'ErrorNumber', ERROR_MESSAGE() AS 'ErrorMessage', ERROR_SEVERITY() AS 'ErrorSeverity', ERROR_STATE() AS 'ErrorState', ERROR_LINE() AS 'ErrorLine'
RETURN ERROR_NUMBER()
END CATCH

END

最后一个部分实际上是一个'OR',它给了我CategoryID和ClassID的代码的联合,不管给定的ClassID是否是给定CategoryID的子代。

我在这里的问题是,如何编写实现这两种情况的条件。

最新样本数据:

情景1

@ CategoryId = 2,5,@ ClassID = 10(这里10是孩子,2是父,CategoryID 2对应ClassID 10,11,12)

预期结果:10,26,27(26和27对应于CategoryID 5)

情景2

@ CategoryID = 2,@ ClassID = 13,15(13和15是不同父级的子级,CategoryID 2对应ClassID的10,11,12)

预期成果:10,11,12,13,15

表dbo.ClasstoCategoryMapping中的数据将如下所示:

CategoryID  ClassID  Code
2           10       200
2           11       201
2           12       202
5           26       501
5           27       502
6           15       601
6           16       602
6           17       603
7           20       701
7           21       702
7           22       703

我想我的问题非常明确,如果不是,那么人们可以让我编辑它。我很乐意这样做。我敦促专家帮我解决这个问题。任何指针也将非常感激。

此致

阿努拉格

4 个答案:

答案 0 :(得分:1)

这是可以实现目标的示例查询,这是您想要的吗?

DECLARE @SAMPLE TABLE
(
ID INT IDENTITY(1,1),
CategoryId INT,
ClassID INT
)

INSERT INTO @sample
VALUES(2,10)
INSERT INTO @sample
VALUES(2,11)
INSERT INTO @sample
VALUES(2,12)
INSERT INTO @sample
VALUES(3,13)

DECLARE @CategoryID INT
DECLARE @ClassID Int

- 在这里玩你的参数

SET @CategoryID = 2
SET @ClassID = 13

- Snenario 1

- @ CategoryId = 2,@ ClassID = 10(这里10是孩子,2是父,CategoryID 2对应ClassID 10,11,12)

- 预期结果:10

IF EXISTS(SELECT * FROM @SAMPLE WHERE CategoryId = @CategoryID AND ClassID = @ClassID)
SELECT ClassID FROM @SAMPLE WHERE CategoryId = @CategoryID AND ClassID = @ClassID

- 情景2

- @ CategoryID = 2,@ ClassID = 13(13是不同父级的子级,CategoryID 2对应ClassID的10,11,12)

- 预期成果:10,11,12,13

ELSE

SELECT ClassID FROM @SAMPLE WHERE ClassID = @ClassID OR CategoryId = @CategoryID

答案 1 :(得分:1)

如果我正确理解了问题,您在结果集中需要的是:

(所有提供的classid)+(所提供的categoryid的所有classid,没有匹配的classid)

这将转化为以下内容:

CREATE PROCEDURE [dbo].[uspGetCodes]
(
    @CategoryID varchar(50),
    @ClassID varchar(50)
)
AS
BEGIN
    SELECT  COALESCE(CM.CategoryID, CM2.CategoryID) AS CategoryID, 
            COALESCE(CM.ClassID, CM2.ClassID) AS ClassID, 
            COALESCE(CM.Code, CM2.Code) AS Code
    --Matched classIDs: 
    FROM    dbo.udfSplitCommaSeparatedIntList(@ClassID) CLAS
    JOIN    dbo.ClassToCategoryMapping CM
            ON  CM.ClassId = CLAS.Value
    --Unmatched CategoryIDs:
    FULL
    OUTER
    JOIN    dbo.udfSplitCommaSeparatedIntList(@CategoryID) CAT
            ON  CM.CategoryID = CAT.Value
    LEFT
    JOIN    dbo.ClassToCategoryMapping CM2
            ON  CM.CategoryID IS NULL
            AND CM2.CategoryID = CAT.Value
END

我在结果中包含了类别,类和代码,因为它更容易看到发生了什么,但我猜你只需要代码

这使用以下函数来分割提供的逗号分隔字符串:

CREATE FUNCTION [dbo].[udfSplitCommaSeparatedIntList]
(
    @Values varchar(50)
)
RETURNS @Result TABLE
(
    Value int
)
AS
BEGIN
    DECLARE @LengthValues int
    SELECT @LengthValues = COALESCE(LEN(@Values), 0)

    IF (@LengthValues = 0)
        RETURN

    DECLARE @StartIndex int
    SELECT @StartIndex = 1

    DECLARE @CommaIndex int
    SELECT @CommaIndex = CHARINDEX(',', @Values, @StartIndex)
    DECLARE @Value varchar(50);

    WHILE (@CommaIndex > 0)
    BEGIN
        SELECT @Value = SUBSTRING(@Values, @StartIndex, @CommaIndex - @StartIndex)
        INSERT @Result VALUES (@Value)

        SELECT @StartIndex = @CommaIndex + 1
        SELECT @CommaIndex = CHARINDEX(',', @Values, @StartIndex)
    END

    SELECT @Value = SUBSTRING(@Values, @StartIndex, LEN(@Values) - @StartIndex + 1)
    INSERT @Result VALUES (@Value)

    RETURN
END

答案 2 :(得分:0)

试试这个

select * from yourtable 
where CategoryId = @CategoryID and ClassID = @ClassID
union
select * from 
(
    select * from yourtable where ClassID = @ClassID 
    union
    select * from yourtable where CategoryId = @CategoryID
) v
where not exists (select * from yourtable where CategoryId = @CategoryID and ClassID = @ClassID)

答案 3 :(得分:0)

删除字符串更新

如果您有逗号分隔的字符串,那么最好使用CLR函数来创建表,但您可以使用SQL函数。使用Google搜索很容易找到如何执行此操作的示例...但是这里有一篇关于此主题的好文章 - > http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings我希望在某些时候在大多数平台上都会有原生支持。

假设您有一个函数返回一个类型为int的列(名为ID)的表,或者在空输入上返回一个空表。注意:您可能必须让null返回一个表,其中一行包含无效值(永远不会加入的值),比如-1。

代码就像这样简单:

SELECT Code
FROM ClassToCategoryMapping
LEFT JOIN MakeTableFromCVS(@CategoryID) AS CatTable 
       ON CatTable.ID = CategoryID
LEFT JOIN MakeTableFromCVS(@ClassID) AS ClassTable 
       ON ClassTable.ID = ClassID
WHERE 
   CASE 
     WHEN @CatgoryID IS NULL THEN -1 ELSE CatTable.ID 
   END = ISNULL(CatTable.ID,-1) 
AND
   CASE
     WHEN @ClassID IS NULL THEN -1 ELSE ClassTable.ID
   END = ISNULL(ClassTable.ID,-1) 
AND
   COALESCE(CatTable.ID,ClassTable.ID,-1) != -1

逻辑与以下相同。因为如果值不为null,则连接将改变值,我们必须使用不同的技巧。这里我们使用标记值(在本例中为-1)来表示空值。任何不会出现在逗号分隔列表中的值都将作为此标记值使用,请记住它必须属于同一类型。


这里不需要动态SQL,如果不使用动态SQL,你会发现SQL服务器更擅长优化。实际上,您甚至不需要if语句如果您可以确定输入始终为null,则可以执行以下操作:

SELECT Code
FROM ClassToCategoryMapping
WHERE 

如何运作

此查询检查CategoryID和ClassID列是否与传入参数匹配,但是当它们为null时通过检查列自身来“忽略”输入。这是一个方便的SQL技巧。

请注意,如果您确实需要检查空字符串,那么这几乎一样快

DECLARE @myCatID varchar(max)
DECLARE @myClassID varchar(max)

SET @myCatID = @CategoryID
SET @myClassID = @ClassID

IF LTRIM(RTRIM(@CategoryID) = '' THEN SET @myCatID = NULL
IF LTRIM(RTRIM(@ClassID) = '' THEN SET @myClassID = NULL

SELECT Code
FROM ClassToCategoryMapping
WHERE CatgoryID = ISNULL(@myCatID,CategoryID) 
  AND ClassID = ISNULL(@myClassID,ClassID)

如果您愿意,可以将ISNULL()替换为COALESCE() ......在这种情况下,他们会做同样的事情。