如何设计订单重要的SQL查找表

时间:2016-09-09 14:16:55

标签: sql sql-server junction-table

我正在尝试创建一个查找表,其中元素的顺序很重要。我的查找表具有以下结构

id table1id table2id 
1     1      1
2     1      2
3     1      3
4     2      2
5     2      1
6     2      3

我的目标是根据table2ids找到table1id。所以我运行的示例查询是

Select table1id 
from junctionTable 
where table2ids in (1,2,3)
group by table1id
Having count(table1id) = 3

然而,这将有效,它将返回Table1Ids 1和2.我只希望Table1ID具有特定顺序的1,2,3,因此它应该只返回TableId = 1。

在表格上放置一个订单栏,但随着项目数量的增加,查询会变得更加困难。

select table1id from junctionTable 
where table2id =1 and order =1 and    
table2id = 2 and order =2 
etc...

还有什么我能做的,我没想到的?或者处理这种情况的最佳方法是什么?

问题源于我试图规范化表格。请参阅Normalize a table with tightly coupled data以获取参考

5 个答案:

答案 0 :(得分:2)

以下查询可以返回您的预期结果:

WITH CTE AS (
    SELECT table1id, table2id, ROW_NUMBER() OVER(PARTITION BY table1id ORDER BY id) AS CustomOrder
    FROM JunctionTable 
    WHERE table2id IN (1, 2, 3)
) 
SELECT table1id 
FROM CTE
WHERE table2id = CustomOrder
GROUP BY table1id
HAVING COUNT(DISTINCT table2id) = 3

使用给定的样本数据进行演示:

DECLARE @JunctionTable TABLE (id INT, table1id INT, table2id INT);

INSERT INTO @JunctionTable (id, table1id, table2id) VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 2, 2),
(5, 2, 1),
(6, 2, 3);

WITH CTE AS (
    SELECT table1id, table2id, ROW_NUMBER() OVER(PARTITION BY table1id ORDER BY id) AS CustomOrder
    FROM @JunctionTable 
    WHERE table2id IN (1, 2, 3)
) 
SELECT table1id 
FROM CTE
WHERE table2id = CustomOrder
GROUP BY table1id
HAVING COUNT(DISTINCT table2id) = 3

答案 1 :(得分:0)

如果顺序很重要,那么必须通过使其成为数据的属性来明确地跟踪排序。在您当前的模型中,订单被隐式存储,并且这不起作用,因为您永远不应该假设SQL存储和返回数据的顺序。

根据你问题中的基表,你可以添加一个varchar来命令“正确的”table2id排序,比如

Id  table1id  table2id  table2order
1       1         1        1,2,3
2       1         2        1,2,3
3       1         3        1,2,3
4       2         2        2,1,3
5       2         1        2,1,3
6       2         3        2,1,3

这当然没有规范化,所以你可以选择第二个表:

table1id  table2order
    1        1,2,3
    2        2,1,3

使用其中一个或另一个应该支持您的目标。

答案 2 :(得分:0)

select table1id 
from 
(
select table1id, (table2id - id) as diff
from junctionTable 
where table2ids in (1,2,3)
)
group by table1id, as diff 
having count(*) = 3 

答案 3 :(得分:0)

为了解决这个问题,您必须创建一个查找表,该表定义了您想要使用的目标table2id的所需顺序,因为IN子句在匹配这两个集合时不会强加订单。

如果你有SQL Server 2012或更高版本,那么你可以使用LAG窗口函数来帮助解决问题,如下所示:

DECLARE @table2IdInOrder TABLE
(
    OrderID  INT IDENTITY(1,1)
    ,table2id INT
);

INSERT INTO @table2IdInOrder
VALUES(2), (1), (3);


WITH CTE_JT AS (
    SELECT   JT.id
            ,JT.table1id
            ,JT.table2id
            ,JT.table2id - LAG(JT.table2id, 1, 0) OVER(PARTITION BY JT.table1id ORDER BY JT.id) AS DeltaID
            ,COUNT(JT.table2id) OVER(PARTITION BY JT.table1id) AS RecordCount
    FROM @junctionTable JT
    WHERE   JT.table2id IN (SELECT table2id FROM @table2IdInOrder)
)
, CTE_OT
AS
(
    SELECT   OT.OrderID
            ,OT.table2id
            ,OT.table2id - LAG(OT.table2id, 1, 0) OVER(ORDER BY OT.OrderID) AS DeltaID
            ,COUNT(OT.table2id) OVER(PARTITION BY (SELECT 1 AS ID)) AS RecordCount
    FROM    @table2IdInOrder OT
)
SELECT      DISTINCT JT.table1id
FROM        CTE_JT JT
INNER JOIN  CTE_OT OT ON OT.table2id = JT.table2id AND OT.DeltaID = JT.DeltaID AND OT.RecordCount = JT.RecordCount;

答案 4 :(得分:0)

此查询检查Id和Table2Id的顺序是否相同。

WITH CTE AS (
    SELECT table1id, table2id
    ,orderIsOK = ROW_NUMBER() OVER(PARTITION BY table1id ORDER BY id) 
               - ROW_NUMBER() OVER(PARTITION BY table1id ORDER BY table2id)   
    FROM JunctionTable 
    WHERE table2id IN (1, 2, 3)
) 
SELECT table1id 
FROM CTE
WHERE orderIsOK = 0
GROUP BY table1id
HAVING COUNT(DISTINCT table2id) = 3

或者,您可能希望明确指定所需的顺序。在下面的示例中,所需的顺序指定为(2,1,3)

WITH desiredOrder(rowNumber, value) as (
    select * 
    from (
        values (1,2)
        ,(2,1)
        ,(3,3)
        ) t (rowNumber, value)
),  
CTE AS (
    SELECT table1id, table2id
    ,orderIsOK = ROW_NUMBER() OVER(PARTITION BY table1id ORDER BY id)
            - do.rowNumber
    FROM @JunctionTable t
    JOIN desiredOrder do ON t.table2id = do.value
) 
SELECT table1id 
FROM CTE
WHERE orderIsOK=0
GROUP BY table1id
HAVING COUNT(DISTINCT table2id) = 3