【问题标题】:How to JOIN on a custom concatenated column如何加入自定义连接列
【发布时间】:2022-01-23 19:50:05
【问题描述】:

Table_E

Client_ID Name Product
2217 John Smith Tablet
3109 Rebecca Anderson Phone
1569 Rajat Singh Laptop

表_Z

Client_ID City Product_ID
TA 2217 Boston xcg153hkk
PH 3109 Houston dfr983uht
LT 1569 Seattle tty455phq
SELECT EE.name
      ,EE.Product
      ,Left(ZZ.Product_ID,4,3) 

  FROM Table_e EE
  Left join Table_z ZZ
  On Case When EE.Product = Tablet then 'TA' + ' ' + EE.Client_ID
          when EE.Product = Phone then 'PH' + ' ' + EE.Client_ID
          when EE.Product = Laptop Then 'LT' + ' ' + EE.Client_ID
          Else 'N/A' ON ZZ.Client_ID 

要加入这两个表,我知道它位于 Client_ID 上。但是,Table_Z 上的 Client_ID 有一个前缀和一个基于您在 Table_E 中看到的产品的“空格”。此外,我想在我的“SELECT”语句中仅从表 Z 中检索 Product_ID 编号。我将如何完成此操作?正确的代码是什么样的?

【问题讨论】:

  • 似乎您应该在Table_Z 中有一个没有前缀的计算列,以便您可以正确创建FOREIGN KEY CONSTRAINT。然后,您也可以轻松JOIN;作为上面的 JOIN 将不是 SARGable,因此不具有性能。
  • 数据库规范化的东西。如果您定义Client_ID,您应该为此保留相同的定义,并将其应用于所有使用Client_ID 的表。在这种情况下,将 Table_Z 中的 Client_ID 拆分为 Client_IDPrefix(或类似的东西......)
  • 真正的问题是Table Z,而不是JOIN。这就是需要解决的问题。一个文件应该只包含 一个 值。 Table_Z.ClientID 应该分成两列,一列包含实际的 ClientID,另一列包含产品代码
  • 把人放在一张桌子上,把产品放在第二张桌子上,把购物放在第三张桌子上。

标签: sql sql-server left-join case


【解决方案1】:

正如Panagiotis Kanavos 所提到的,前缀根本不应该存在。您正在使用前缀表示第二条信息,但该信息已在表Table_E 中;不要这样做。确实,您应该完全删除前缀,更改数据类型并在Table_Z 中的Client_ID 上创建FOREIGN KEY 约束。修复你的设计,解决问题

如果由于某种原因您无法执行此操作,请使用计算列。在下文中,我假设Table_E 中的ClientIDint,既可以是PRIMARY KEY,也可以用作UNIQUE INDEX

首先,创建新列:

ALTER TABLE dbo.Table_Z ADD Client_ID_np AS TRY_CONVERT(int,STUFF(Client_ID,1,CHARINDEX(' ',Client_ID),'')) PERSISTED;

此新列删除前缀并尝试将值转换为int(如果无法转换则返回NULL)。

然后让我们创建FOREIGN KEY

ALTER TABLE dbo.Table_Z ADD CONSTRAINT FK_TableZ_TableE_ClientID FOREIGN KEY (Client_ID_np) REFERENCES Table_E (ClientID);

这样可以确保没有无效值。如果这失败了,那么我的假设之一是错误的,或者你有错误的数据。在继续之前在这里解决这个问题。

然后(根据需要解决数据完整性问题后)您可以进行简单的连接:

SELECT {Your Columns}
FROM dbo.Table_E E
     JOIN dbo.Table_Z Z ON E.Client_ID = Z.Client_ID_np;

【讨论】:

  • 感谢您的回复,我很欣赏这种方法。但是,这种方法会导致一系列不同的问题,我不会在这里详细说明,但其中一个是前缀为客户端 ID 中的数字增加了灵活性,客户端 ID 中只有 9999 种不同的组合,所以前缀是随着产品继续销售,能够在不向 ID 添加额外数字的情况下区分它们......有没有办法通过在 Table_E 中创建前缀来加入表格,因为我们知道它将基于 Product 列以便它可以连接使用 Table_Z?
  • 那么我推荐你第一段,@shade206 或 TL;DR:修复你的设计,修复问题
  • Table_E 列出了产品名称,但 Table_Z 没有。 Table_Z 通过与 Client_ID 关联的 Prefix 指示产品。无法删除前缀的原因是在 Table_Z 中可能有另一个 Client_ID 为“2217”,但不是平板电脑,即“TA 2217”,它可能是笔记本电脑,即“LT 2217”,在 Table_E 中没有问题,因为产品列区分数字。在 Table_Z 中,如果您删除前缀,您将不知道是笔记本电脑 2217 还是平板电脑 2217 彼此配对。所以我想在 Table_E 中创建人工前缀并与 Table_Z 配对的原因是为了确保正确配对
  • 是的,所以修正你的设计;你显然需要 3 张桌子,而不是 2 张,@shade206 .#
【解决方案2】:

我认为您正在尝试做的是这样的事情。我不确定你想用 case 表达式完成什么,但如果我理解你只是想加入两个表而不担心 table_z 上的前缀,那么我认为你应该试试这个。

SELECT e.client_id, e.name, e.product, z.product_id
FROM table_e as e
JOIN table_z as z ON
    e.client_id = SUBSTRING(z.client_id, CHARINDEX(' ', z.client_id), LEN(z.client_id))

我为您创建了一个 sqlfiddle 来验证这个答案。 http://sqlfiddle.com/#!18/5453ad/2/0

我现在明白以上内容对您不起作用,所以这里有一个新建议。

我将创建一个描述产品及其关联代码的连接表,并将其命名为 product_codes。应该是这样的。

CREATE TABLE product_codes(
    product varchar(20),
    product_code char(2)
)

我还会更新您的 table_z 以将 client_id 和 product_code 分隔到单独的字段中。这样您就可以像这样编写查询。

SELECT e.client_id, e.name, e.product, z.product_id
FROM table_e AS e
    JOIN product_codes AS pc ON e.product = pc.product
    JOIN table_z AS z 
        ON e.client_id = z.client_id 
        AND pc.product_code = z.product_code

但是,我假设您不能以任何方式更改数据库的结构。如果是这种情况,那么您可以使用 CTE 为您的查询创建一个 product_codes 表。

WITH product_codes(product, product_code) AS (
  SELECT 'Laptop' as product, 'LT' as product_code
  UNION
  SELECT 'Phone' as product, 'PH' as product_code
  UNION
  SELECT 'Tablet' as produt, 'TA' as product_code
)
SELECT e.client_id, e.name, e.product, z.product_id
FROM table_e as e
JOIN product_codes pc 
  ON e.product = pc.product
JOIN table_z as z 
  ON pc.product_code + ' ' + CONVERT(CHAR(7), e.client_id) = z.client_id

我已经更新了 sqlfiddle 供你试用。

http://sqlfiddle.com/#!18/5453ad/11

【讨论】:

  • 这个解决方案对你有用吗?
【解决方案3】:

正如其他人所指出的,最好的解决方案是更改您的设计。如果这是不可能的(不是您的设计等),我建议使用 CTE 连接所需的字段,然后加入。如果 Table_E Client_ID 字段为 INT,则需要在 CTE 的 CASE 表达式中使用 CASTCONVERT

DECLARE @Table_E TABLE (Client_ID VARCHAR(20), C_Name VARCHAR(50), Product VARCHAR(20))
INSERT INTO @Table_E
VALUES
(2217, 'John Smith', 'Tablet'),
(3109, 'Rebecca Anderson', 'Phone'),
(1569, 'Rajat Singh', 'Laptop')

DECLARE @Table_Z TABLE (Client_ID VARCHAR(20), City VARCHAR(20), Product_ID VARCHAR(20))
INSERT INTO @Table_Z
VALUES
('TA 2217', 'Boston', 'xcg153hkk'),
('PH 3109', 'Houston', 'dfr983uht'),
('LT 1569', 'Seattle', 'tty455phq');

WITH Client_IDZ AS
(
    SELECT Client_ID,
        C_Name,
        Product,
        CASE WHEN EE.Product = 'Tablet' THEN 'TA'+ ' '+ EE.Client_ID
             WHEN EE.Product = 'Phone' THEN 'PH'+ ' '+ EE.Client_ID
             WHEN EE.Product = 'Laptop' THEN 'LT'+ ' '+ EE.Client_ID
             ELSE NULL END AS Client_IDZ
    FROM @Table_E EE
)



SELECT EE.C_Name
      ,EE.Product
      ,ZZ.Product_ID
FROM Client_IDZ EE
  LEFT JOIN @Table_z ZZ ON EE.Client_IDZ = ZZ.Client_ID
C_Name Product Product_ID
John Smith Tablet xcg153hkk
Rebecca Anderson Phone dfr983uht
Rajat Singh Laptop tty455phq

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-07
    • 1970-01-01
    • 1970-01-01
    • 2021-10-19
    • 2019-05-26
    • 1970-01-01
    • 2018-01-15
    • 2012-07-04
    相关资源
    最近更新 更多