Выберите строки, значения которых совпадают в 2 других столбцах

#sql #tsql

#sql #tsql

Вопрос:

Таблица

 Id   ||   IdFrom  ||  IdTo
--------------------------
1    ||    null   ||  2
2    ||    1      ||  null
3    ||    null   ||  5
4    ||    null   ||  6
5    ||    3      ||  9
6    ||    4      ||  7
7    ||    6      ||  null
8    ||    null   ||  null
9    ||    5      ||  10
10   ||    9      ||  null
947  ||    null   ||  949
949  ||    947    ||  952
950  ||    null   ||  951
951  ||    950    ||  952
952  ||    951    ||  null
  

Необходимо получить все строки или только конкретные идентификаторы, которые находятся между всеми 3 столбцами при указании данного идентификатора. Итак, ВЫБЕРИТЕ (все идентификаторы, найденные в IdFrom или IDto, и эти IdFrom или IDto находятся в других IdFrom или IDto)

Результаты при поиске по идентификатору 1 выдадут результаты с идентификаторами 1 и 2

Результаты при поиске идентификатора 2 выдадут результаты с идентификаторами 1 и 2

Результаты при поиске по идентификатору 3,5,9 или 10 выдадут результаты с идентификаторами 3,5,9 и 10

Результаты при поиске идентификатора 4,6 или 7 выдадут результаты с идентификаторами 4,6 и 7

Мой текущий поиск представляет собой итерацию получения IdFrom и IDto для Id, помещения найденных идентификаторов в таблицу tmp и повторения поиска совпадений до тех пор, пока не будут найдены более четкие идентификаторы. Это работает, но крайне некрасиво и занимает больше времени, чем, вероятно, могло бы…

Наткнулся на запрос, который может получить все строки, которые имеют совпадение, но не указаны для определенного идентификатора

 DECLARE @SearchForId int = 1
SELECT 
    t1.ID,t1.IdFROM,t1.IdTO
FROM SomeTable t1
WHERE
    (
        EXISTS(SELECT Id FROM SomeTable tblFROM WHERE tblFROM.IdFROM = t1.Id) OR
        EXISTS(SELECT Id FROM SomeTable tblTO WHERE IdTO.IDTRANSFEREDTO = t1.Id)
    )  
AND Id = 1 <<-- this part just gives that id obvious but without it, it gets everything in the entire table
  

РЕДАКТИРОВАТЬ: добавлены новые идентификаторы (947-952). Предыдущее выбранное решение действительно предоставляло идентификаторы 947 и 949, но отсутствовало 950,951,952. Попытался добавить еще пару cte, подобных предыдущему решению, чтобы присвоить всем идентификаторам 947,949,950,951,952, но выдать только 947 и 949. Как получить все 5. Это решение было намного быстрее, почти в 25 раз. Хотелось бы сохранить его и получить идентификаторы остатка

Комментарии:

1. Вы могли бы попробовать и, по крайней мере, поделиться этим, даже если это не работает. В любом случае..

2. Извините за сделанную правку

Ответ №1:

Вам нужно сделать это, используя два рекурсивных общих табличных выражения.

 declare @id int
set @id = 2  

;WITH CTE1 
    AS(
        SELECT c.*
        FROM tbl c
        WHERE c.Id = @id

        UNION ALL

        SELECT p.*
        FROM CTE1 cte1_alias
        INNER JOIN  tbl p 
          ON p.IdFrom = cte1_alias.Id
    ),
    CTE2
    AS(
        SELECT c.*
        FROM tbl c
        WHERE c.Id = @id

        UNION ALL

        SELECT p.*
        FROM CTE2 cte2_alias
        INNER JOIN  tbl p 
          ON p.IdTo = cte2_alias.Id
    )

SELECT Id FROM CTE1
Union 
SELECT Id FROM CTE2
  

Комментарии:

1. Будет указан только 1 идентификатор. При поиске идентификатора 3 выдается 3,5,9 … или при поиске 5 выдается 3,5,9, или при поиске 9 выдается 3,5,9

2. похоже, пока это работает хорошо на 20 или около того столбцах, которые я пробовал. По какой-либо причине выполняется select * ?? Было бы лучше или быстрее просто выбрать необходимые столбцы в cte?

3. Вы можете упростить задачу, исключив столбцы, которые не используются в joins , однако, поскольку их слишком мало, это не будет иметь большого значения.

4. Это сработало очень хорошо. После запуска с полной таблицей в ней отсутствовали некоторые идентификаторы. Смотрите редактирование, должно получиться 947,949,950,951,952, но получаем только 947 и 949

5. Отредактировал сообщение. Текущее решение хорошо работало для большинства. Необходимо получить другие идентификаторы, которые решение не нашло

Ответ №2:

Насколько я понимаю, было бы достаточно ИЛИ того, чтобы столбцы были равны заданному значению:

 Declare @prm int = 1
Select Id
From SomeTable
Where Id=@prm Or IdFrom=@prm Or IdTo=@prm
  

или, для другого варианта, используйте in среди столбцов

 Declare @prm int = 1
Select Id
From SomeTable
Where @prm in (Id, IdFrom, IdTo)