Существует ли UNION, эквивалентный FULL OUTER JOIN в T-SQL, который будет объединять разнородные наборы столбцов?

Я пытаюсь выполнить следующее:

SELECT col1, col2 FROM table1
UNION
SELECT col2, col3 FROM table2

В результате:

col1, col2, col3
1   , 1   , NULL
NULL, 1   , 1

Возвращается объединение столбцов и строк. Вы можете думать об этом как об UNION, эквивалентном FULL OUTER JOIN.

Простой ответ на этот вопрос:

SELECT col1, col2, NULL AS col3 FROM table1
UNION
SELECT NULL AS col1, col2, col3 FROM table2

Это, однако, трудно сделать с динамическим T-SQL и dbo.sp_executesql, и результирующий запрос может быть слишком длинным, если задействовано большое количество UNIONS.

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

CREATE TABLE #temp ( col1 int, col2 int, col3 int )
INSERT INTO #temp ( col1, col2 ) SELECT col1, col2 FROM table1
INSERT INTO #temp ( col2, col3 ) SELECT col2, col3 FROM table2

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

Есть ли более простой способ сделать это?

Спасибо!


person Kuyenda    schedule 06.12.2009    source источник
comment
Ваш комментарий: Но для этого нужно заранее знать имена и типы столбцов. Но разве это не подразумевается и тем, чего вы пытаетесь достичь своим начальным примером? Как вы можете вернуть (два разных) столбца col2 из двух разных таблиц, не зная, что их типы совпадают?   -  person davek    schedule 06.12.2009
comment
В моем конкретном сценарии я знаю, что столбцы, которые я пытаюсь сопоставить по имени, также совпадают по типу. Итак, мой вопрос предполагает, что типы совпадают. Я отредактирую вопрос, чтобы указать это.   -  person Kuyenda    schedule 06.12.2009


Ответы (4)


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

Этот запрос даст вам список столбцов и их типов данных для конкретной таблицы:

SELECT @columnName = c.name AS columnName,
       @columnDataType = ty.name AS columnDataType
  FROM SYS.TABLES ta
  JOIN SYS.COLUMNS c ON c.object_id = ta.object_id
  JOIN SYS.TYPES ty ON ty.user_type_id = c.user_type_id
 WHERE ta.name = '[your_table_name]'

Это заполняет переменные @columnName и @columnDataType, чтобы вы могли использовать их в другом месте.

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

Более простым подходом было бы определить, какой запрос необходим для большинства ситуаций.
Печально то, что когда вы спрашиваете, ответ обычно "все". Для этого ответа я бы изложил вещи, которые для поддержки всего, что вам нужно, это количество валюты и времени. Подчеркните, что дешевле и быстрее разбить требование на управляемые части — это также позволит вам получить обратную связь. Возможно, малым набором функционала никто никогда не воспользуется, или это вообще не то, что нужно пользователю. Научитесь управлять ожиданиями клиентов.

person OMG Ponies    schedule 06.12.2009
comment
Спасибо за запрос на получение имен столбцов и типов данных. Я бы хотел, чтобы использовать их в запросе было так же просто, как и извлекать их. - person Kuyenda; 07.12.2009
comment
Спасибо за обновление, но когда я сказал, что хотел бы, чтобы их было так же легко использовать, я имел в виду тот факт, что вы не можете использовать переменную в сочетании с оператором языка определения данных. Например, вы не можете сделать CREATE TABLE #temp (@columnName @columnDataType). - person Kuyenda; 07.12.2009

После быстрой попытки самое близкое, что я могу получить в T-SQL, не прыгая через множество пылающих обручей хакерства, - это использовать FULL OUTER JOIN с ложным условием... например.

DECLARE @TableA TABLE (Col1 INTEGER IDENTITY(1,1), Col2 INTEGER)
DECLARE @TableB TABLE (Col2 INTEGER IDENTITY(1,1), Col3 INTEGER)

INSERT @TableA VALUES (1)
INSERT @TableA VALUES (11)
INSERT @TableB VALUES (2)
INSERT @TableB VALUES (222)

SELECT *
FROM @TableA a
    FULL OUTER JOIN @TableB b ON 0 = 1

Однако из результатов вы увидите, что Col2 дважды включен в набор результатов... он не объединен в один.

Мое предложение состояло бы в том, чтобы обработать это в вашем коде, который сидит над этим, вместо того, чтобы пытаться это сделать в T-SQL - вернуть несколько наборов результатов в ваш вызывающий код и иметь дело с разными схемами. Если вам действительно нужно это как один набор результатов, может ли этот вызывающий код не объединить результаты в один?

person AdaTheDev    schedule 06.12.2009
comment
Я также исследовал FULL OUTER JOIN с ложным условием, но, как вы говорите, это приводит к дублированию столбцов. Мои обстоятельства требуют, чтобы я возвращал единый сводный набор результатов из T-SQL. Спасибо! - person Kuyenda; 06.12.2009
comment
Под пылающими обручами хакерства вы имеете в виду динамический маршрут T-SQL и dbo.sp_executesql? Похоже, это единственный доступный вариант. - person Kuyenda; 07.12.2009
comment
Да, я просто думаю, что если вам приходится прыгать через обручи, чтобы достичь цели, вам следует подумать, можно ли немного изменить фактическую цель, чтобы сделать ее более эффективной. Я буду продолжать думать. - person AdaTheDev; 07.12.2009
comment
Под небольшим изменением положения вы имеете в виду правильную нормализацию базы данных. Да, я поднял вопрос нормализации, когда мне впервые показали схему. Скажем так, вам нужно разбить несколько яиц, чтобы сделать омлет. Думаю, мне следует начать погружаться в мир динамического T-SQL. Откровенно говоря, я ненавижу NVARCHAR и прочее. - person Kuyenda; 07.12.2009

Поможет ли это (присоединение к какой-то фиктивной колонке) дать некоторое представление?

declare @t1 table(col1 varchar(10),col2 varchar(10))
declare @t2 table(col2 varchar(10),col3 varchar(10))
insert into @t1 select '1','1' union all select '10','1'
insert into @t2 select '1',null union all select '10','1'

;with cte1 as(select ROW_NUMBER() over(order by getdate()) rn1, t1.* from @t1 t1)
,cte2 as(select ROW_NUMBER() over(order by getdate()) rn2, t2.* from @t2 t2)
select c1.col1,c1.col2,c2.col3
from cte1 c1 
full outer join cte2 c2
on c1.rn1 = c2.rn2

Вывод:

col1    col2    col3
1   1   NULL
10  1   1
person priyanka.sarkar    schedule 07.12.2009

Я бросил полотенце и написал кучу неприятных динамических T-SQL и выполнил их с помощью sp_executesql. Спасибо за помощь!

person Kuyenda    schedule 13.12.2009