【问题标题】:How to join more than two tables on the basis of two parameters?如何根据两个参数连接两个以上的表?
【发布时间】:2015-01-15 06:38:14
【问题描述】:

我有三个表#temptbProductstbVendor

tbVendor(States, AgentId, Products)

tbProduct(ProductId, ProductName)

#temp(ProductName)

样本数据

tbVendor
-------------------------------- ----------- --------------------
States                           AgentId     Products
-------------------------------- ----------- --------------------
Alabama,New York                 1           T.V, Desktops
New Jersy, Florida               2           Cellphones, Laptops
New York, San Fransico           1           Cellphones, Desktop
New Jersy, San Fransico          1           A.C, Heaters

tbProduct
----------- -------------
ProductId   ProductName
----------- -------------
1           T.V
2           Laptops
3           Desktop
4           Cellphones
5           A.C
6           Heaters

#temp
---------------
ProductName
---------------
T.V
Laptops
Desktop
Cellphones
A.C
Heaters

现在我已经对它们执行连接操作以获取 ProductIdProductName
Agent IdStates 字段的基础上分配tbVendor

例如,我在New yorkAgentId = 1 中获取了所有ProductsNamesIds,输出应该是这样的

ProductId   ProductName
----------- -------------
1           T.V
3           Desktop
4           Cellphones

请注意,#Temp 包含分配给AgentId = 1 的所有产品。不知何故,我设法获取分配给AgentId = 1 的所有产品,并将它们放在#temp 表中。现在我无法设法获取输出,例如仅分配给New York 的产品预期输出如下所示

ProductId   ProductName
----------- -------------
1           T.V
3           Desktop
4           Cellphones

我试过下面的代码行它不起作用

Select * from tbProduct L
JOIN #Temp TL  ON L.ProductName collate SQL_Latin1_General_CP1_CI_AS = TL.ProductName
JOIN tbVendor Li ON Li.AgentId = @AgentId and Li.States like @States;

注意@States 包含New York@AgentId 包含1

【问题讨论】:

  • 你不能重新设计数据库shema吗?目前,您有状态和产品的逗号分隔列表 - 这使得任何连接都非常困难(您必须使用字符串操作)并且无效。
  • 我知道这个逗号造成了很多麻烦......但我没有设计这个架构......如果我这样做,我必须重新工作多年的工作由其他开发者完成。完成整个项目需要很长时间...请帮助我
  • tbVendor 的结构违反了 First NF,需要规范化。您将逗号分隔的列表存储在一列中,这有很多缺点。例如,您将无法存储比列中适合的列表更长的列表。编辑:好的,现在我明白了..
  • 您能否在问题中包含预期输出的示例?
  • 检查上面的问题...我已经标记了预期的输出

标签: sql sql-server sql-server-2008 join


【解决方案1】:

写成:

;WITH cte AS (
    SELECT 
        AgentId,
        CAST('<r>' + REPLACE(States, ',', '</r><r>') + '</r>' AS XML) AS States,
        CAST('<r>' + REPLACE(Products, ',', '</r><r>') + '</r>' AS XML) AS Products
    FROM @tbVendor
)
,FinalList AS (
SELECT 
    AgentId,
    RTRIM(LTRIM (sTable.sColumn.value('.', 'VARCHAR(MAX)'))) AS States,
    RTRIM(LTRIM (PTable.PColumn.value('.', 'VARCHAR(MAX)'))) AS Products
FROM cte
CROSS APPLY States.nodes('//r') AS sTable(sColumn) 
CROSS APPLY Products.nodes('//r') AS PTable(PColumn) 
)
SELECT DISTINCT F.Products AS ProductName
       ,T.ProductId AS ProductId       
FROM FinalList F
CROSS APPLY (SELECT ProductId FROM @tbProduct TP WHERE TP.ProductName = F.Products) AS T
WHERE F.States = 'New York'
AND F.AgentId = 1
ORDER BY T.ProductId ASC

更新:处理特殊字符 LIKE &amp;amp; 将其替换为 &amp;amp; 为:

;WITH cte AS (
    SELECT 
        AgentId,
        CAST('<r>' + REPLACE(States, ',', '</r><r>') + '</r>' AS XML) AS States,
        CAST('<r>' + REPLACE(REPLACE(Products,'&','&amp;'), ',', '</r><r>') + '</r>' AS XML) AS Products
    FROM @tbVendor
)
,FinalList AS (
SELECT 
    AgentId,
    RTRIM(LTRIM (sTable.sColumn.value('.', 'VARCHAR(MAX)'))) AS States,
    RTRIM(LTRIM (PTable.PColumn.value('.', 'VARCHAR(MAX)'))) AS Products
FROM cte
CROSS APPLY States.nodes('//r') AS sTable(sColumn) 
CROSS APPLY Products.nodes('//r') AS PTable(PColumn) 
)
SELECT DISTINCT F.Products AS ProductName
       ,T.ProductId AS ProductId       
FROM FinalList F
CROSS APPLY (SELECT ProductId FROM @tbProduct TP WHERE TP.ProductName = F.Products) AS T
WHERE F.States = 'New York'
AND F.AgentId = 1
ORDER BY T.ProductId ASC

DEMO

【讨论】:

  • 伟大的努力......它工作正常,但有一些产品保存为美容和护肤品,它正在抛出 XML 解析:第 1 行,字符 100,此类产品的非法名称字符错误消息
  • 您必须将 & 替换为 &amp,就像其他特殊字符一样,您也必须先替换 ..
  • 非常感谢先生...非常感谢您的回答是出色的解决方案
  • 这个'-'字符怎么样,对不起,我忘了提
【解决方案2】:

试试这个:

DECLARE @state varchar(25) = 'New York',
@agentId int = 1

SELECT STUFF((SELECT ', ' + Products 
        FROM tbVendor tv 
        WHERE tv.AgentId = @agentId AND tv.States Like  '%' +@state + '%'
        FOR XML PATH('')), 1, 2, '') AS products
INTO #tmp

;with tmp(product, products) as (
select  LTRIM(LEFT(products, CHARINDEX(', ',products+', ')-1)),
    STUFF(products, 1, CHARINDEX(', ',products+', '), '')
from #tmp
union all
select  LTRIM(LEFT(products, CHARINDEX(', ',products+', ')-1)),
    STUFF(products, 1, CHARINDEX(', ',products+', '), '')
from tmp
where products > ''
)
SELECT tp.*
FROM tbProduct tp
INNER JOIN tmp t ON t.product = tp.ProductName

【讨论】:

    【解决方案3】:

    将 CSV 拆分为行

    ;WITH CTE AS
    (   
        -- Split States
        SELECT ROW_NUMBER() OVER(ORDER BY (SELECT(0))) RNO, 
        AgentId,
        LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'STATE'    
        FROM  
        (
             -- Use 3 REPLACE for  '|', ';', ','
             SELECT AgentId,
             CAST ('<M>' + REPLACE([STATE], ',', '</M><M>') + '</M>' AS XML) AS Data         
             FROM #tbVendor
    
        ) AS A 
        CROSS APPLY Data.nodes ('/M') AS Split(a)
    )   
    SELECT C1.AGENTID,C1.[STATE],TAB.Products,P.ProductID     
    FROM CTE C1
    JOIN
    (   
        -- Split Products
        SELECT ROW_NUMBER() OVER(ORDER BY (SELECT(0))) RNO, AgentId,
        LTRIM(RTRIM(Split.A.value('.', 'VARCHAR(100)'))) 'Products' 
        FROM  
        (
             -- Use 3 REPLACE for  '|', ';', ','
             SELECT AgentId,         
             CAST ('<M>' + REPLACE(Products, ',', '</M><M>') + '</M>' AS XML) AS Data      
             FROM #tbVendor
    
        ) AS A 
        CROSS APPLY Data.nodes ('/M') AS Split(a)
    )TAB
    ON C1.RNO=TAB.RNO AND C1.AGENTID=TAB.AGENTID
    -- Get Product Id
    JOIN #tbProduct P
    ON TAB.Products=P.ProductName
    WHERE ...your conditions...
    

    现在您将获得所有statesProductIdProductNameAgentId

    【讨论】:

      猜你喜欢
      • 2016-11-18
      • 2021-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-23
      • 2021-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多