好的,我在产品表中有一些数据,其中包含一个类别ID列,该列仅包含类别列表中使用的4个可用字符中的2个。
示例:产品的类别ID为“AA”,但可以来自桌面(AAA)或服务器(AAB)类别。
在类别表中,我们有3列包含帮助我们的信息:include,atrID和valID。 valID列包含逗号分隔的值列表(例如,“K01819”或“K00846,K00851”),它们可能只包含一个或多个ID。 atrID列包含类似的字符串(例如'A00432')。 include列是1或0,基于很久以前来过的程序员完成的一些工作。
基本上他已经浏览了各种表格,发现对于某些类别(例如AA),我们可以从类别表中加入另一个表,并使用atrID和valID列表将它们全部绑定。
示例:
产品“a”的catID为AA,是台式机。这属于'AAA'类别,其中包括0,atrID为'A00432',valID为'K01819'。
获取类别信息的示例SQL:
SELECT cat.atrID, cat.valID, cat.[include]
FROM db.dbo.cat AS cat
WHERE cat.catID = 'AAA'
然后我会将这些变量保存在.Net代码中,并根据include的值构建SQL。我还会将valID拆分并作为单独的参数传递,从而导致在SQL服务器上执行以下代码。
SELECT DISTINCT prod.prodID
FROM db.dbo.prod AS prod
INNER JOIN db.dbo.atr AS atr ON atr.prodID = prod.prodID
WHERE prod.catID = 'AA'
AND atr.atrID = 'A00432'
AND atr.valID NOT IN('K01819')
要为服务器更改此设置,我会将NOT IN
更改为IN
,因为服务器(AAB)的包含值为1.示例:
SELECT DISTINCT prod.prodID
FROM db.dbo.prod AS prod
INNER JOIN db.dbo.atr AS atr ON atr.prodID = prod.prodID
WHERE prod.catID = 'AA'
AND atr.atrID = 'A00432'
AND atr.valID IN('K01819')
我使用IN
和NOT IN
,因为某些类别在valID列中有多个值。
我的问题是,如果我想在不知情的情况下获取特定产品的类别,它甚至可能吗? (未能从您认为的类别中获取所有产品的列表,并在其中一个或另一个列表中对其进行匹配)。我一直在告诉我的老板,我现在几天都无法解决这个问题,而且我没有到达任何地方。任何帮助将不胜感激。
编辑:
我要做的是获取任何产品的类别。我将上面的SQL作为我获取桌面和服务器类别产品列表的示例。
我想反过来,即当我只有产品表中的两个字母类别时,从类别表中获取3个字母的类别。
编辑2:
产品表列:
prodID varchar(40)
catID char(2)
示例行:
'S101010','AA'(桌面)
'S202020','AA'(服务器)
'S303030','ED'(激光打印机)
'S404040','ED'(喷墨打印机)
类别表列:
catID varchar(4)
description varchar(50)
包括位(实际上是一个tinyint,它应该显示我每天处理的内容= P)
atrID varchar(6)
valID varchar(250)
示例行:
'AAA','桌面',0,'A00432','K01819'
'AAB','服务器',1,'A00432','K01819'
'EDA','激光打印机',1,'A00172','K00846,K00851'
'EDB','喷墨打印机',1,'A00172','K00845'
属性表列:
prodID varchar(40)
catID char(2)
atrID varchar(10)
valID varchar(10)
示例行:
'S101010','AA','A00432','K01817'
'S202020','AA','A00432','K01819'
'S303030','ED','A00172','K00846'
'S303030','ED','A00172','K00851'
'S404040','ED','A00172','K00845'
我上面提到的是当我知道category.catID时如何获得产品列表。我想要的是当我只有product.prodID和product.catID时才能获得该类别。
我是在经历这样的事情,但它不完整,不会按原样运作。然后我会检查catIn.catID和catNotIn.catID的结果,看看哪一个不是null,那将是我的类别,但我无法弄清楚连接。如果有可能的话。并且它没有考虑具有两个以上变体的类别。
SELECT prod.prodID, catIn.catID, catNotIn.catID
FROM product AS prod
INNER JOIN attributes AS atr ON atr.prodID = prod.prodID
LEFT OUTER JOIN category AS catIn ON LEFT(catIn.catID, 2) = prod.catID AND catIn.atrID = atr.atrID --more conditions needed here
LEFT OUTER JOIN category AS catNotIn ON LEFT(catNotIn.catID, 2) = prod.catID AND catIn.atrID = atr.atrID --more conditions needed here
我希望这能解释我的目标。
答案 0 :(得分:0)
我首先要说你的数据模型非常糟糕。您不应该有一个逗号列表中需要进行拆分和匹配的值。它应该是一个表关系。但我离题了......
因此,唯一的区别在于您是否根据IN
的值使用NOT IN
vs include
,那么如果我要在服务器上完全写这个,我会这样做:
首先,给自己一个漂亮的CSV分割UDF。 how to split and insert CSV data into a new table in single statement?这是一个很好的例子 - 简单明了。
然后,查询(我将变成存储过程)可能看起来像这样:
CREATE PROCEDURE SprocNameHere
@prodID INT
AS
-- I'm making assumptions about the data types here. Fix them accordingly
DECLARE @atrID INT
DECLARE @valID varchar(MAX)
DECLARE @include BIT
DECLARE @catID varchar(50)
-- given the passed in product ID, look up @catID
SELECT @catID = catID FROM prod WHERE prodID = @prodID
SELECT @atrID = cat.atrID, @valID = cat.valID, @include = cat.[include]
FROM db.dbo.cat AS cat
WHERE cat.catID = @catID
SELECT DISTINCT prod.prodID
FROM db.dbo.prod AS prod
INNER JOIN db.dbo.atr AS atr ON atr.prodID = prod.prodID
LEFT JOIN dbo.inline_split_me(',', @valID) ism ON ism.Value = atr.valID
WHERE prod.catID = 'AA' -- is this hardcoded or should this come from the query above?? How do you convertt 'AAA' to 'AA'? What Table/relationship/concept does this?
AND atr.atrID = @atrID
AND ((@include = 0 AND ism.Value IS NULL) OR (@include = 1 AND ism.Value IS NOT NULL))
很难确保我的上面的语法是准确的,因为我没有完整的工作模型。但这是一般的想法:您的UDF inline_split_me
将记录拆分为“表”,然后您可以加入该表。通过使用LEFT JOIN,如果atr
表之间没有匹配,则ism.Value将为null。这可用于执行NOT IN
。相反,对于IN
模拟也是如此。
编辑1:根据您的反馈,这是我想出的试验台。我没有完整的数据视图,但我认为这是你正在寻找的(或至少接近)。评论,我会调整。
根据您的示例数据,我在tempdb中运行了这个。由于我没有EDB的属性记录,所以我不能确定我是否回到了我应该回来的东西。我还得到两条记录S303030,一条用于属性表中的每个valID。你可以使用DISTINCT只返回一个,如果你只是寻找ProdID + Category.CatID的唯一性,或者如果有一块我不理解的谜题,请告诉我,我会调整我的答案。这适用于除Ink Jets之外的所有内容,因为您的示例数据在#Category表中没有喷墨记录。如果我伪造一个说UNION SELECT 'S404040', 'ED', 'A00172', 'K00845'
,那么它似乎有效。
--DROP TABLE #Product
--DROP TABLE #Category
--DROP TABLE #Attribute
--DROP FUNCTION inline_split_me
GO
CREATE FUNCTION inline_split_me (@SplitOn char(1),@String nvarchar(max))
RETURNS @results TABLE(Part NVARCHAR(MAX))
AS
BEGIN
IF (CHARINDEX(@SplitOn,@String)-1<= 0)
INSERT INTO @results
SELECT @string AS Part
ELSE
WITH SplitSting AS
(
SELECT
LEFT(@String,CHARINDEX(@SplitOn,@String)-1) AS Part
, CASE WHEN LEN(@String)-CHARINDEX(@SplitOn,@String) > 0 THEN RIGHT(@String,LEN(@String)-CHARINDEX(@SplitOn,@String)) ELSE NULL END AS Remainder
WHERE @String IS NOT NULL AND CHARINDEX(@SplitOn,@String)-1>0
UNION ALL
SELECT
LEFT(Remainder,CHARINDEX(@SplitOn,Remainder)-1)
,RIGHT(Remainder,LEN(Remainder)-CHARINDEX(@SplitOn,Remainder))
FROM SplitSting
WHERE Remainder IS NOT NULL AND CHARINDEX(@SplitOn,Remainder)-1>0
UNION ALL
SELECT
Remainder,null
FROM SplitSting
WHERE Remainder IS NOT NULL AND CHARINDEX(@SplitOn,Remainder)=0
)
INSERT INTO @results
SELECT Part FROM SplitSting
RETURN
END
GO
CREATE TABLE #Product
(
prodID varchar(40)
, catID char(2)
)
CREATE TABLE #Category
(
catID varchar(4)
, description varchar(50)
, include bit --(actually a tinyint which should go to show what I deal with on a daily basis =P)
, atrID varchar(6)
, valID varchar(250)
)
CREATE TABLE #Attribute
(
prodID varchar(40)
, catID char(2)
, atrID varchar(10)
, valID varchar(10)
)
INSERT INTO #Product
SELECT 'S101010', 'AA'
UNION SELECT 'S202020', 'AA'
UNION SELECT 'S303030', 'ED'
UNION SELECT 'S404040', 'ED'
INSERT INTO #Category
SELECT 'AAA', 'Desktops', 0, 'A00432', 'K01819'
UNION SELECT 'AAB', 'Servers', 1, 'A00432', 'K01819'
UNION SELECT 'EDA', 'Laser Printers', 1, 'A00172', 'K00846,K00851'
UNION SELECT 'EDB', 'Inkjet Printers', 1, 'A00172', 'K00845'
INSERT INTO #Attribute
SELECT 'S101010', 'AA', 'A00432', 'K01817'
UNION SELECT 'S202020', 'AA', 'A00432', 'K01819'
UNION SELECT 'S303030', 'ED', 'A00172', 'K00846'
UNION SELECT 'S303030', 'ED', 'A00172', 'K00851'
-- make sure it works with just one parameter
SELECT * FROM dbo.inline_split_me(',', 'K00846')
-- so you can see the split
SELECT * FROM dbo.inline_split_me(',', 'K00846,K00851')
-- so you can see the split in reference to the category
SELECT c.*, Part
FROM #Category c
OUTER APPLY dbo.inline_split_me(',', c.valID) split
SELECT p.*, a.*, c.*
FROM #Product p
INNER JOIN #Attribute a ON a.prodID = p.prodID AND a.catID = p.catID
INNER JOIN #Category c ON c.atrID = a.atrID
AND (
(c.include = 0 AND a.valID NOT IN (SELECT Part FROM dbo.inline_split_me(',', c.valID)))
OR (c.include = 1 AND a.valID IN (SELECT Part FROM dbo.inline_split_me(',', c.valID)))
)
ORDER BY a.ProdID, a.atrID--, c.catID
DROP TABLE #Product
DROP TABLE #Category
DROP TABLE #Attribute
DROP FUNCTION inline_split_me