2012-11-09 75 views
1

好的,我在產品表中有一些數據,其中包含的類別ID列僅包含分類列表中使用的4個可用字符中的2個。SQL加入時遇到問題

示例:產品的類別ID爲'AA',但可能來自桌面(AAA)或服務器(AAB)類別。

在類別表中,我們有3列包含幫助我們的信息:include,atrI​​D和valID。 valID列包含逗號分隔的值列表(例如「K01819」或「K00846,K00851」),其中可能只包含一個ID或更多。 atrID列包含一個相似的字符串(例如'A00432')。包含列可以是1或0,這取決於程序員完成的一些工作,這些工作是在很久以前發生的。

基本上,他通過各種表格已經蒐羅後發現,某些類別(如AA),我們可以從類別表加入另一個表並使用valIDs的atrID和清單,以配合全身。

例:

產品「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代碼保存這些變量並建立取決於包括值的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') 

我使用INNOT IN因爲有些類別在valID列中有多個值。

我的問題是,如果我想在沒有預先知道的情況下獲得特定產品的類別,甚至有可能嗎? (如果沒有獲得您認爲其所屬類別中的所有產品的列表,並將其與其中一個或另一個列表匹配)。我一直在告訴我的老闆,我現在無法解決這些問題,而且我也沒有找到任何地方。任何幫助將不勝感激。

編輯:

我想要做的是得到任何產品的類別。我將上面的SQL作爲我如何獲得桌面和服務器類別產品列表的一個例子。

我想要反過來,即從產品表中只有兩個字母類別時,即從類別表中獲取3個字母的類別。

編輯2:

產品表列:
PRODID VARCHAR(40)
CATID炭(2)

樣品行:
'S101010', 'AA'(桌面)
'S202020','AA'(服務器)
'S303030','ED'(激光打印機)
'S404040','ED'(噴墨打印機)

分類表列:
CATID VARCHAR(4)
描述VARCHAR(50)
包括位(這應該去展示我的處理上每天= P實際上是一個TINYINT)
atrID VARCHAR(6 )
有效VARCHAR(250)

樣品行:
'AAA', '桌面',0, 'A00432', 'K01819'
'AAB', '服務器',1, 'A00432', 'K01819'
'EDA','激光打印機',1,'A00172','K00846,K00851'
'EDB', '噴墨打印機',1, 'A00172', 'K00845'

屬性表中的列:
PRODID VARCHAR(40)
CATID炭(2)
atrID VARCHAR(10)
有效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

自動清理不良數據是一個令人難以置信的挑戰。您的產品表中必須包含一些可以與類別表匹配的信息。 prod.catID被SUPPOSED是。如果你不能這樣匹配,你將不得不創建一個顯示用戶所有可能匹配的屏幕(JOIN prod cat ID AA ON catID LIKE'AA%'),並給他們選擇哪個選項是正確的。儘可能簡化,但讓用戶清理自己的不良數據。 –

+0

不幸的是,這不是一個選項。我不得不加入這個行列,因爲我們正在實施一個新系統來根據不同客戶的價格來調整他們的客戶經理的擺佈。我需要找出類別,因爲他們可能會爲類別指定標記,但不指定個別產品,我需要確保價格與客戶經理在瀏覽產品時預期的內容相符。如果它只是那些模糊不清的類別,那麼它就不會是這樣的問題,但是因爲它是臺式機和服務器,所以我有點尷尬。 – Sean

+0

客戶特定的標記應存儲在一個單獨的表格中。你一定需要調整你的數據模型。產品 - >產品類別(產品類型),客戶 - >客戶類別(針對客戶類型)。然後,您需要將product_categories映射到customer_categories,以便您的AM可以按產品或按產品類別設置客戶組的標記......或按產品或產品類別爲特定客戶設置標記。我假設你db.dbo.cat用於產品類別。 –

回答

0

我會先說你的數據模型很糟糕。您不應該有需要分割和匹配的值的逗號列表。它應該是一個表關係。但我離題了...

因此,如果唯一的區別就是你是否使用IN VS NOT IN基礎上,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將爲空。這可以用來做NOT IN。相反,對於IN模擬也是如此。

編輯1:根據您的反饋,這裏是我想出的測試牀。我沒有完整的數據視圖,但我認爲這就是你正在尋找的(或者至少接近)。評論和我會調整。

鑑於您的示例數據,我在tempdb中運行了這個。由於我沒有教育局的屬性記錄,所以我無法確定我是否回來了我應該回來的東西。我還找回兩條記錄S303030,一個用於屬性表中的每個valID。如果您只是在ProdID + Category.CatID中尋找唯一性,或者如果有一塊我不明白的難題,請告訴我,我會調整我的答案,您可以使用DISTINCT取回一個。這適用於除噴墨機之外的所有內容,因爲您的示例數據在#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 
+0

對不起,我可能不夠清楚。嘗試獲取特定產品的數據時,我手邊沒有這個類別。我包含了我用來獲取產品列表的SQL,以查看它是否可以被顛倒以用於獲得任何產品的類別。 – Sean

+0

根據您的意見,我稍微改變了我的回覆。我仍然對如何從'AAA'到'AA'有疑問。如果你能解決這個問題,這個解決方案對你有意義嗎? –

+0

是的,數據是非常糟糕的,但我無能爲力,這是來自CNet的反饋。這些類別是在很久以前定義的,從那時起,每個人都習慣於爲AAA和AAB單獨列出。另一個系統可以工作,因爲它循環遍歷類別以獲得應用的任何標記,並將價格保存到不同的表格中。它總是知道他們屬於哪個類別。不幸的是,我被告知這樣做不是一種選擇,因爲客戶經理有可能爲每個客戶端上的每個產品添加標記。 – Sean