Как разработать таблицу поиска SQL, где порядок имеет значение

Я пытаюсь создать таблицу поиска, в которой порядок элементов имеет значение. Моя таблица поиска имеет следующую структуру

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

Моя цель - найти table1id на основе table2ids. Итак, пример запроса, который я запускаю,

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

Однако это работает, он вернет как Table1Id 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...

Могу ли я сделать что-то еще, о чем я не думаю? Или как лучше поступить в этой ситуации?

Вопрос возник из-за того, что я пытался нормализовать таблицы. См. Нормализовать таблицу с сильно связанными данными для справки.


person Brandon    schedule 09.09.2016    source источник
comment
ты знаешь, что делает having?   -  person Ven    schedule 09.09.2016
comment
Отражается ли желаемый порядок в столбце id? Если нет, то да, вам нужен другой столбец для определения порядка. Таблицы сами по себе не имеют внутреннего порядка.   -  person Damien_The_Unbeliever    schedule 09.09.2016
comment
@Ven да, я знаю. Я обновил свой вопрос, включив в него свою группу, которую забыл. Я включил наличие, потому что у меня может быть 1 или только 2 идентификатора, и мне нужны только те, у которых 3 или сколько угодно в предложении in.   -  person Brandon    schedule 09.09.2016


Ответы (4)


Следующий запрос может вернуть ожидаемый результат:

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
person Arulkumar    schedule 09.09.2016
comment
Это решение основано на том, что запрашиваемые id являются конкретно 1,2 и 3. Оно не будет работать для произвольных значений, а также если искомый набор, например, 2,1,3. - person Damien_The_Unbeliever; 09.09.2016
comment
@Damien_The_Unbeliever Здесь для table1id = 2 текущий порядок равен 2,1,3, а результат не учитывает table1id = 2 - person Arulkumar; 09.09.2016
comment
Но он ломается на 234. Я считаю на 1-х и не пропущенных. Эй, это работает на 123 +1 - person paparazzo; 09.09.2016

Если порядок имеет значение, то его нужно явно отслеживать, сделав его атрибутом данных. В вашей текущей модели порядок сохраняется неявно, и это не работает, потому что вы никогда не должны делать никаких предположений о порядке, в котором 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

Использование того или иного из них должно способствовать достижению ваших целей.

person Philip Kelley    schedule 09.09.2016
comment
@Phillip Будет ли создание столбца varChar, в котором указан порядок, более эффективной / лучшей практикой базы данных, чем создание столбца Order, который является int? - person Brandon; 09.09.2016
comment
В конечном счете, это зависит от того, как обрабатываются данные. Что определяет правильный порядок для table1id? Если это регулярно определяется, управляется, работает с набором строк в таблице, тогда решение столбца может быть лучшим (хотя такие запросы становятся довольно уродливыми). Если он регулярно хранится и работает с ним как с некой формой простого списка с разделителями, тогда подходит идея с одним тегом атрибута (и имхо было бы намного проще работать). - person Philip Kelley; 09.09.2016
comment
@Филипп Спасибо за помощь. Этот вопрос возник при попытке нормализовать таблицу, в которой у меня было 3 столбца идентификаторов, в которых отслеживался порядок, поскольку все данные находились в одной строке. Однако я хотел сделать это более динамичным и не создавать новый столбец каждый раз, когда мы хотим отслеживать другой материал. Если подумать, это должен был быть мой вопрос с самого начала. - person Brandon; 09.09.2016

Чтобы решить эту проблему, вам нужно будет создать таблицу поиска, которая определяет желаемый порядок целевых table2id, которые вы хотите использовать, поскольку INclause не устанавливает порядок при сопоставлении двух наборов.

Если у вас есть 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;
person Edmond Quinton    schedule 09.09.2016

Этот запрос проверяет, совпадает ли порядок 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
person Serg    schedule 09.09.2016