开发者

SQL IF ELSE with 2 variables and JOIN 3 tables

I am a C# programmer who has to write some SQL stored procedures, but I am not very good at SQL. I have a stored procedure that will return a list of SKUs based on search criteria with two variables, and the variables can be NULL if the user did not select one of the search items. The list will return a list of SKU rows that have a matching column for the search variable(s), plus the name of the table linked. I have built a database that has the following three tables, which should explain it better:

CREATE TA开发者_Python百科BLE [dbo].[SKU](
[SKU_ID] [int] IDENTITY(1,1) NOT NULL,
[CATEGORY_ID] [int] NULL,
[MANUFACTURER_ID] [int] NULL,
[SKU_NUMBER_CD] [varchar](100) NOT NULL,
[ACTIVE_IND] [bit] NOT NULL

CREATE TABLE [dbo].[CATEGORY](
[CATEGORY_ID] [int] IDENTITY(1,1) NOT NULL,
[CATEGORY_NUMBER_CD] [varchar](10) NULL,
[CATEGORY_NAME_TXT] [varchar](255) NULL,
[ACTIVE_IND] [bit] NOT NULL

CREATE TABLE [dbo].[MANUFACTURER](
[MANUFACTURER_ID] [int] IDENTITY(1,1) NOT NULL,
[MANUFACTURER_NAME_TXT] [varchar](50) NULL,
[ACTIVE_IND] [bit] NOT NULL

This is the stored procedure I am trying to get working. What I want to do is have 4 different cases, for when there is a value in @CategoryID and @ManufacturerID, when one of them is NULL but the other has a value, and when both of them are NULL. In Case 1, I want to return only those SKUs that have a matching Category AND matching Manufacturer, for Case 2 I want to return SKUs that have a matching Category but manufacturer doesn't matter, and so on. The problem I am having is that I still need to grab the manufacturer_name_txt from the Manufacturer table for those rows returned in Case 2. But I am getting each row that should be returned multiple times, each with a different manufacturer name in that column.

    ALTER PROCEDURE [dbo].[usp_SAL_GetSkuListFiltered]
    @CategoryID int,
    @ManufacturerID int
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN
    --Get SKUs that have a matching Category and a matching Manufacturer
    IF @CategoryID IS NOT NULL AND @ManufacturerID IS NOT NULL
        SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
        FROM SKU k, CATEGORY c, MANUFACTURER m
        WHERE k.CATEGORY_ID = @CategoryID 
            AND k.MANUFACTURER_ID = @ManufacturerID
            AND k.CATEGORY_ID = c.CATEGORY_ID
            AND k.MANUFACTURER_ID = m.MANUFACTURER_ID
            AND k.ACTIVE_IND = 1


    --Get SKUs that have a matching Category and any Manufacturer
    ELSE IF @CategoryID IS NOT NULL AND @ManufacturerID IS NULL
        SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
        FROM SKU k, CATEGORY c, MANUFACTURER m
        WHERE k.CATEGORY_ID = @CategoryID
            AND k.CATEGORY_ID = c.CATEGORY_ID
            AND k.MANUFACTURER_ID = m.MANUFACTURER_ID
            AND k.ACTIVE_IND = 1


    --Get SKUs that have a matching Manufacturer and any Category
    ELSE IF @CategoryID IS NULL AND @ManufacturerID IS NOT NULL
        SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
        FROM SKU k, CATEGORY c, MANUFACTURER m
        WHERE k.MANUFACTURER_ID = @ManufacturerID
            AND k.CATEGORY_ID = c.CATEGORY_ID
            AND k.MANUFACTURER_ID = m.MANUFACTURER_ID
            AND k.ACTIVE_IND = 1

    -- Get all SKUs
    ELSE
        SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
        FROM SKU k, CATEGORY c, MANUFACTURER m
        WHERE k.CATEGORY_ID = c.CATEGORY_ID
            AND k.MANUFACTURER_ID = m.MANUFACTURER_ID
            AND k.ACTIVE_IND = 1
    END
END

Here is a sample of the data:

SKU_ID/CATEGORY_ID/MANUFACTURER_ID/SKU_NUMBER_CD/MANUAL_IND/ACTIVE_ID

1   1   1   BFG56789    False   True

2   1   2   YIR46578    False   True

3   1   3   WER22987    False   True

4   1   3   WXT87654    False   True

5   2   4   TYR44578    False   True

6   2   1   DRE44559    False   True

7   3   2   ZXX44455    False   True

8   4   3   BWE44590    False   True

9   4   2   HGT347474   False   True

12  NULL    NULL    12344   False   True

13  2   NULL    473489  False   True

14  3   NULL    437HHH8 False   True

15  NULL    1   YUXD678 False   True

16  NULL    3   WEW3334 False   True

As an example, when I execute the stored proc with these variables:

EXEC    @return_value = [dbo].[usp_SAL_GetSkuListFiltered]
    @CategoryID = 2,
    @ManufacturerID = NULL

I will get two rows back, SKU_IDs 5 and 6, but not SKU_ID 13 which has a NULL in the MANUFACTURER_ID field. But if I remove a line from the stored proc so I have this:

--Get SKUs that have a matching Category and any Manufacturer
ELSE IF @CategoryID IS NOT NULL AND @ManufacturerID IS NULL
    SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
    FROM SKU k, CATEGORY c, MANUFACTURER m
    WHERE k.CATEGORY_ID = @CategoryID
        AND k.CATEGORY_ID = c.CATEGORY_ID
        AND k.ACTIVE_IND = 1

Then I get 12 rows returned - SKU_ID 5 four times, each with a different manufacturer name (I have 4 listed in the MANUFACTURER table), then SKU_ID 6 four times, and SKU_ID 13 four times.

I know from looking around the site that using the WHERE syntax can be ambiguous. I am using SQL 2005. I know that I need to use LEFT or RIGHT JOINs, but I do not understand them. I have tried many different things over the past several days with no results. I just don't "get" SQL queries. Can someone help me figure out where my logic is wrong?


You can probably make the entire thing work like this:

SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
FROM SKU k
LEFT JOIN CATEGORY c
ON k.CATEGORY_ID = c.CATEGORY_ID
LEFT JOIN MANUFACTURER m
ON k.MANUFACTURER_ID = m.MANUFACTURER_ID
AND k.ACTIVE_IND = 1
WHERE k.CATEGORY_ID = ISNULL(@CategoryID, k.CATEGORY_ID)
AND k.MANUFACTURER_ID = ISNULL(@ManufacturerID, k.MANUFACTURER_ID)


The notation you use is implicit joins, which are all INNER JOIN. Your NULL rows are left out because the criteria specifies an equality and the ID on the left for manufacturer is NULL. When a criteria is left out, you get a CROSS JOIN (INNER JOIN on TRUE), which is why you see multiplication of rows.

Try something like this (no need for multiple IF statements):

SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
FROM SKU k, CATEGORY c, MANUFACTURER m
WHERE (k.CATEGORY_ID = @CategoryID OR @CategoryID IS NULL)
    AND (k.MANUFACTURER_ID = @ManufacturerID OR @ManufacturerID IS NULL)
    AND (k.CATEGORY_ID = c.CATEGORY_ID OR k.CATEGORY_ID IS NULL)
    AND (k.MANUFACTURER_ID = m.MANUFACTURER_ID OR k.MANUFACTURER_ID IS NULL)
    AND k.ACTIVE_IND = 1

Although, I recommend getting away from implicit joins.

I would recommend you look at learning more about SQL, there are learning questions here which recommend books.

With explicit joins:

SELECT k.*, c.CATEGORY_NAME_TXT, m.MANUFACTURER_NAME_TXT
FROM SKU k
LEFT JOIN CATEGORY c -- A left join means a row in K has to exist, but c can be missing
    ON k.CATEGORY_ID = c.CATEGORY_ID
LEFT JOIN MANUFACTURER m
    ON k.MANUFACTURER_ID = m.MANUFACTURER_ID
WHERE (k.CATEGORY_ID = @CategoryID OR @CategoryID IS NULL)
    AND (k.MANUFACTURER_ID = @ManufacturerID OR @ManufacturerID IS NULL)
    AND k.ACTIVE_IND = 1

Note that a left join can still result in a cross-join-like multiplication effect if multiple rows exist on the right.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜