SQL-在同一列中找到父项

时间:2019-01-07 13:38:20

标签: sql sql-server tsql

我无法正确表达问题以在线找到答案,所以我希望有人可以向我提供解决方案的链接,因为我认为这是一项相当常见的任务。

我们的产品种类繁多,想确定父母的身份。 所有产品名称均在同一列中,其逻辑如下

ProductId ProductName
-----------------------------------------------------------------------------
1         ABC
2         ABCD
3         ABCD1
4         ABCD2

结果应为

ABCD1和ABCD2 ABCD 的子代, ABCD ABC

的子代
ProductId ProductName ParentName ParentId
------------------------------------------------------------------------------
1         ABC         NULL       NULL
2         ABCD        ABC        1
3         ABCD1       ABCD       2
...

5 个答案:

答案 0 :(得分:3)

嗯。我认为这可以满足您的要求:

select p.*, pp.ProductName as parentName, pp.ProductId as parentId
from products p outer apply
     (select top (1) pp.*
      from products pp
      where p.ProductName like pp.ProductName + '%' and
            p.ProductId <> pp.ProductId
      order by len(pp.ProductName) desc
     ) pp;

答案 1 :(得分:1)

If there's only 1 character difference.
Then you can LEFT JOIN to the ProductName & one wildcard character '_'

SELECT 
 p1.ProductId, 
 p1.ProductName, 
 p2.ProductName AS ParentName, 
 p2.ProductId AS ParentId
FROM Products p1
LEFT JOIN Products p2 ON p1.ProductName LIKE CONCAT(p2.ProductName,'_')
ORDER BY p1.ProductId;

Example snippet:

declare @Products table (
  ProductId INT primary key identity(1,1), 
  ProductName varchar(30) not null, 
  unique (ProductName)
);

insert into @Products (ProductName) values
 ('ABC')
,('ABCD')
,('ABCD1')
,('ABCD2')
;

SELECT 
 p1.ProductId, 
 p1.ProductName, 
 p2.ProductName AS ParentName, 
 p2.ProductId AS ParentId
FROM @Products p1
LEFT JOIN @Products p2 ON p1.ProductName LIKE CONCAT(p2.ProductName,'_')
ORDER BY p1.ProductId;

Result:

ProductId   ProductName ParentName  ParentId
1           ABC         NULL        NULL
2           ABCD        ABC         1
3           ABCD1       ABCD        2
4           ABCD2       ABCD        2

If it's possible that there's more than 1 character difference then:

SELECT TOP (1) WITH TIES
 p1.ProductId, 
 p1.ProductName, 
 p2.ProductName AS ParentName, 
 p2.ProductId AS ParentId
FROM Products p1
LEFT JOIN Products p2 ON p1.ProductName LIKE CONCAT(p2.ProductName,'_%')
ORDER BY ROW_NUMBER() OVER (PARTITION BY p1.ProductId ORDER BY LEN(p2.ProductName) DESC);

答案 2 :(得分:1)

毫无疑问,戈登的答案在这里是最好的,但我仍然同意:

USE TEMPDB

CREATE TABLE #T (ProductID INT, ProductName VARCHAR (100))
INSERT INTO #T VALUES (1, 'ABC'), (2, 'ABCD'), (3, 'ABCD1'), (4, 'ABCD2')

WITH CTE AS 
(
SELECT T.*,
      T2.ProductID AS ParentID,
      T2.ProductName AS ParentName
FROM #T AS T
CROSS JOIN #T AS T2
WHERE T.ProductName LIKE T2.ProductName + '%'
  AND T.ProductID <> T2.ProductID
)
, CTE2 AS 
(
SELECT TOP 1 T.*,
      NULL AS ParentID,
      NULL AS ParentName
FROM #T AS T
ORDER BY LEN (T.ProductName)
)

SELECT * FROM CTE UNION ALL SELECT * FROM CTE2 ORDER BY 1

答案 3 :(得分:0)

您尝试将Case与条件一起使用,并将每个条件表示为新列。您可以参考语法https://www.w3schools.com/sql/sql_case.asp

答案 4 :(得分:0)

You can use Common Table Expression (CTE) to do the job.

with product_table (ProductId, ProductName) as
(
    select 1 ProductId         , 'ABC' ProductName union all
    select 2 ProductId         , 'ABCD' ProductName union all
    select 3 ProductId         , 'ABCD1' ProductName union all
    select 4 ProductId         , 'ABCD2' ProductName --union all
)
,product_result (ProductId, ProductName, ParentName, ParentId) as
(
    select ProductId, ProductName, convert(varchar,null) ParentName,     convert(int, null) ParentId
    from product_table 
    where ProductName = 'ABC' --start with 
    union all
    select d.ProductId, d.ProductName, convert(varchar,p.ProductName)     ParentName, p.ProductId ParentId
    from product_table d
    , product_result p
    where d.ProductName like p.ProductName+'_'
)
select *
from product_result

The first part product_table must be replaced by your own product table. It is used here to generate a tempory dataset.

Your final query will look like:

with product_result (ProductId, ProductName, ParentName, ParentId) as
(
    select ProductId, ProductName, convert(varchar,null) ParentName,     convert(int, null) ParentId
    from <YOUR_PRODUCT_TABLE_GOES_HERE> 
    where ProductName = 'ABC' --start with 
    union all
    select d.ProductId, d.ProductName, convert(varchar,p.ProductName)     ParentName, p.ProductId ParentId
    from <YOUR_PRODUCT_TABLE_GOES_HERE> d
    , product_result p
    where d.ProductName like p.ProductName+'_'
)
select *
from product_result

CTE is available since SQL2008. for more info WITH common_table_expression (Transact-SQL)