【问题标题】:SQL Joining on Field with Nulls空值字段的 SQL 连接
【发布时间】:2017-08-28 18:25:20
【问题描述】:

我正在尝试匹配两个表,其中一个表将多个值存储为字符串。

在下面的示例中,我需要使用 #NewProduct.NewProductId 对从 #Orders 表中订购的每个产品进行分类。

我遇到的问题是有时我们会推出一款新产品,例如“Black Shirt”, 后来我们推出了对该产品的改编,例如“Black Shirt Vneck”。

我需要将这两个更改正确匹配到#Orders 表。因此,如果订单有黑色和衬衫,但没有 Vneck,则视为“黑色衬衫”,但如果订单有黑色和衬衫和 Vneck,则视为“黑色 Vneck 衬衫”。

下面的代码是一个示例 - 我正在使用的当前逻辑返回具有左连接的重复项。 另外,假设我们可以修改#NewProducts 的格式,但不能修改#Orders。

IF              OBJECT_ID('tempdb.dbo.#NewProducts') IS NOT NULL DROP TABLE #NewProducts
CREATE TABLE    #NewProducts 
(
      ProductType VARCHAR(MAX)
    , Attribute_1 VARCHAR(MAX)
    , Attribute_2 VARCHAR(MAX)
    , NewProductId INT
)

INSERT      #NewProducts
VALUES
    ('shirt', 'black', 'NULL', 1),
    ('shirt', 'black', 'vneck', 2),
    ('shirt',  'white', 'NULL', 3)


IF              OBJECT_ID('tempdb.dbo.#Orders') IS NOT NULL DROP TABLE #Orders
CREATE TABLE    #Orders
(
      OrderId INT
    , ProductType VARCHAR(MAX)
    , Attributes    VARCHAR(MAX)
)

INSERT  #Orders
VALUES
    (1, 'shirt', 'black small circleneck'),
    (2, 'shirt', 'black large circleneck'),
    (3, 'shirt', 'black small vneck'),
    (4, 'shirt', 'black small vneck'),
    (5, 'shirt', 'white large circleneck'),
    (6, 'shirt', 'white small vneck')

SELECT      *
FROM        #Orders o
        LEFT JOIN #NewProducts np
            ON o.ProductType = np.ProductType
            AND CHARINDEX(np.Attribute_1, o.Attributes) > 0
            AND (
                    CHARINDEX(np.Attribute_2, o.Attributes) > 0
                OR np.Attribute_2 = 'NULL'
                )

【问题讨论】:

  • 这不会有好的结局。规范化您的数据,否则您将陷入字符串匹配地狱(甚至不考虑所有错误和所有性能问题)。你可以有一个PRODUCTS、一个PRODUCT_ATTRIBUTES 和一个ATTRIBUTES 表,然后根据SELECT * FROM orders INNER JOIN products ON orders.product = products.id AND EXISTS (SELECT 1 FROM product_attributes INNER JOIN attributes ON product_attributes.product = attributes.id WHERE product_attributes.product = products.id AND attributes.name = "Black") AND EXISTS (SELECT 1 ... WHERE attributes.name = "Vneck") 做一些事情
  • 这将是理想的,但我受工程团队的摆布,他们将属性字段存储为单个字符串。我可以通过拆分字符串来制作一个临时#attributes 表,然后使用您的方法。这行得通吗?
  • 不是对您问题的回答,而只是评论。因为您在NULL 周围加上了引号,所以您将在相关位置插入单词“NULL”。这不是NULL 的用途。您可以不加引号,但您还需要对查找 NULLs 的代码进行相应更改。
  • 两个直接的问题:“其中一个表将多个值存储为字符串”和“使用空值连接字段”即您的数据违反了 1NF,这是关系模型的最基本要求和编写能力一个简单的 SQL 查询(选择的答案证实了这一点)。要求您的工程团队做得更好!

标签: sql join string-matching


【解决方案1】:

你似乎想要最长的重叠:

SELECT *
FROM #Orders o OUTER APPLY
     (SELECT Top (1) np.*
      FROM #NewProducts np
      WHERE o.ProductType = np.ProductType AND
            CHARINDEX(np.Attribute_1, o.Attributes) > 0
      ORDER BY ((CASE WHEN CHARINDEX(np.Attribute_1, o.Attributes) > 0 THEN 1 ELSE 0 END) +
                (CASE WHEN CHARINDEX(np.Attribute_2, o.Attributes) > 0 THEN 1 ELSE 0 END)
               ) DESC
     ) np;

我不能说我很高兴有必要这样做。 Orders 似乎应该包含引用实际产品的 numeric id。但是,我可以看到有时需要这样做。

【讨论】:

  • 这必须接近@GordonLinoff,但我收到错误The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
  • @SteveLovell 。 . .那里应该有一个TOP (1),因为你只想要一个匹配的行。
  • 这行得通。它很慢,但它会完成工作。谢谢!
【解决方案2】:

我无法得到 Gordon 的回答,当他进来时,我自己的回答也只是其中一部分。他提出的最大重叠的想法有所帮助。我已经调整了您的 NewProducts 表,以便即使 Orders 表不能做到这一点,事情的这一面也会“正常化”。代码如下或rextester.com/ERIF13021

create table #NewProduct
(
NewProductID int primary key,
ProductType varchar(max),
ProductName varchar(max)
)

create table #Attribute
(
AttributeID int primary key,
AttributeName varchar(max)
)

create table #ProductAttribute
(
NewProductID int,
AttributeID int
)

insert into #NewProduct
values (1, 'shirt', 'black shirt'),
       (2, 'shirt', 'black vneck shirt'),
       (3, 'shirt', 'white shirt')

insert into #Attribute
values (1, 'black'),
       (2, 'white'),
       (3, 'vneck')

insert into #ProductAttribute
values (1,1),
       (2,1),
       (2,3),
       (3,2)


select top 1 with ties
*
from
(
select
    o.OrderId,
    p.NewProductID,
    p.ProductType,
    p.ProductName,
    o.Attributes,
    sum(case when charindex(a.AttributeName,o.Attributes)>0 then 1 else 0 end) as Matches
from
    #Orders o
    JOIN #Attribute a ON 
    charindex(a.AttributeName,o.Attributes)>0
    JOIN #ProductAttribute pa ON
    a.AttributeID = pa.AttributeID
    JOIN #NewProduct p ON
    pa.NewProductID = p.NewProductID AND
    o.ProductType = p.ProductType
group by
    o.OrderId,
    p.NewProductID,
    p.ProductType,
    p.ProductName,
    o.Attributes
) o2
order by
row_number() over (partition by o2.OrderID order by o2.Matches desc)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    相关资源
    最近更新 更多