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.
精彩评论