SELECT INTO табличная переменная в T-SQL


372

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

Кроме того, нельзя использовать табличную переменную с запросами SELECT INTO или INSERT EXEC. http://odetocode.com/Articles/365.aspx

Краткий пример:

declare @userData TABLE(
                        name varchar(30) NOT NULL,
                        oldlocation varchar(30) NOT NULL
                       )

SELECT name, location
INTO @userData
FROM myTable
    INNER JOIN otherTable ON ...
WHERE age > 30

Данные в табличной переменной позже будут использоваться для вставки / обновления их в разные таблицы (в основном это копирование тех же данных с незначительными обновлениями). Цель этого состоит в том, чтобы сделать скрипт немного более читабельным и более легко настраиваемым, чем SELECT INTOнепосредственно в правильные таблицы. Производительность не является проблемой, так как rowcountона довольно мала и запускается только вручную, когда это необходимо.
... или просто скажи мне, если я все делаю неправильно.

Ответы:


601

Попробуйте что-то вроде этого:

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData (name, oldlocation)
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;

2
Если в качестве значений, которые вы будете вставлять в таблицу UserData, вы выбираете «SELECT name, location FROM myTable», не имеет значения, соответствуют ли имена переменных в select именам в определении таблицы. Вы выбираете «имя», чтобы войти в переменную «Имя» UserData, но вы выбираете «местоположение» и каким-то образом назначаете его переменной «oldlocation» UserData. Будет ли SQL просто сопоставлять их автоматически или это будет исключение?
Аран Малхолланд

Неважно, имя, только тип столбца.
CristiC

5
Вау, это имеет смысл, но в то же время парсер во мне чувствует себя оскорбленным :)
Аран Малхолланд

Кажется, я не могу использовать это в утверждении (ях) ОБНОВЛЕНИЯ: суть ссылки
Пол-Себастьян Маноле

1
В операторе вставки, если вы не объявляете столбцы явно, тогда они отображаются в порядке, объявленном в исходном операторе создания таблицы, так же, как это делает select *. Таким образом, расположение в операторе выбора отображается на oldlocation в таблице @userData, поскольку расположение находится в позиции 2 в наборе результатов выбора, а oldlocation - это столбец 2 в определении таблицы. Тем не менее, никогда не делай этого. Упорядочение базы данных столбцов или строк не следует полагаться. Всегда будьте откровенны об этом.
Absmiths

94

Цель SELECT INTO(в соответствии с документами, мой акцент)

Чтобы создать новую таблицу из значений в другой таблице

Но у вас уже есть целевой стол! Итак, что вы хотите, это

INSERTОператор добавляет один или несколько новых строк в таблицу

Вы можете указать значения данных следующими способами:

...

Используя SELECTподзапрос, чтобы указать значения данных для одной или нескольких строк, например:

  INSERT INTO MyTable 
 (PriKey, Description)
        SELECT ForeignKey, Description
        FROM SomeView

И в этом синтаксисе разрешено MyTableбыть табличной переменной.


1
Действительно желаю, чтобы принятый ответ включал эту информацию!
Дэви Браун

Я получаю, что MyTable «Неверное имя объекта» делает это, поэтому в этом ответе чего-то не хватает.
Майк Флинн

@MikeFlynn MyTable- это заполнитель для имени вашей фактической таблицы . Я не думаю, что есть реальные базы данных с таблицей с именем MyTable...
AakashM

А если я хочу создать / объявить табличную переменную с помощью SELECT INTO ...? Например, чтобы определить столбцы табличной переменной как t1.somecolumn, t1.othercolumn, t2. *
Армандо

27

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

WITH userData (name, oldlocation)
AS
(
  SELECT name, location 
  FROM   myTable    INNER JOIN 
         otherTable ON ...
  WHERE  age>30
)
SELECT * 
FROM   userData -- you can also reuse the recordset in subqueries and joins

Люблю это! Спасибо.
четверг, в четверг, 18

Я не думаю, что это делает копию, если вы удалите или обновите из userData, он не будет удалять и обновлять записи в ваших исходных таблицах?
atreeon

Да, DELETE и UPDATE на CTE изменят исходную таблицу, если CTE не ссылается на несколько таблиц с помощью объединений, объединений и т. Д.
nanestev

2
Недостатком этого является то, что вы можете использовать таблицу CTE только в следующих командах. Если по какой-либо причине вам нужно выполнить несколько проходов через набор результатов, CTE не будет работать. OP, по-видимому, подразумевает, что будет сделано несколько модификаций, и в этом случае это не сработает: «Данные в табличной переменной позже будут использоваться для вставки / обновления их в разные таблицы (в основном, для копирования тех же данных с незначительными обновления) «.
Тони

16

Вы можете попробовать использовать временные таблицы ... если вы не делаете это из приложения. (Это может быть нормально, чтобы запустить это вручную)

SELECT name, location INTO #userData FROM myTable
INNER JOIN otherTable ON ...
WHERE age>30

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

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

DECLARE @userData TABLE(
    name varchar(30) NOT NULL,
    oldlocation varchar(30) NOT NULL
);

INSERT INTO @userData
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;

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


2
Извините, забыл упомянуть, у меня нет прав на CREATE TABLE.
Индрек

6
Создание темпов имеет немного больше накладных расходов.
Папараццо

2
использование временной таблицы не всегда безопасно. Например, веб-сервисы. С веб-сервисами с одним соединением, которое ограничивает максимальное количество соединений на сервере И защищает SQL немного больше, временная таблица будет существовать для КАЖДОГО проходящего запроса и может перезаписать того, кто ее использует в настоящее время.
Франк

12
@Franck - если вы используете глобальную временную таблицу (два префикса хеша), вы правы. Однако локальная временная таблица (один префикс хеша) будет изолирована для одного сеанса (так называемое одиночное соединение), поэтому не будет проблем параллелизма, на которые вы ссылаетесь, если только вы не используете одно соединение для всех запросов (не рекомендуется). Тем не менее, возможные последствия для производительности остаются.
maf748

@GazB Конечно, любое утверждение с побочным эффектом исключено из использования в function. По моему опыту, в большинстве случаев, когда кто-то считает, что ему нужны такие заявления, на самом деле это означает, что он должен переосмыслить свои function- или, по крайней мере, рефакторинг procedure. Говоря за себя, по крайней мере. :-)
underscore_d


5

Сначала создайте временную таблицу:

Шаг 1:

create table #tblOm_Temp (

    Name varchar(100),
    Age Int ,
    RollNumber bigint
)

** Шаг 2: ** Вставьте некоторое значение в временную таблицу.

insert into #tblom_temp values('Om Pandey',102,1347)

Шаг 3: Объявите переменную таблицы для хранения данных временной таблицы.

declare   @tblOm_Variable table(

    Name Varchar(100),
    Age int,
    RollNumber bigint
)

Шаг 4: выберите значение из временной таблицы и вставьте в переменную таблицы.

insert into @tblOm_Variable select * from #tblom_temp

Наконец, значение вставляется из временной таблицы в переменную таблицы

Шаг 5: Можно проверить вставленное значение в табличной переменной.

select * from @tblOm_Variable

1

Хорошо, теперь с достаточным усилием я могу вставить в @table, используя следующие:

ВСТАВИТЬ @TempWithheldTable ВЫБОР
a.SuspendedReason, a.SuspendedNotes, a.SuspendedBy, a.ReasonCode ОТ OPENROWSET (BULK 'C: \ Databases \ WithHeld.csv', FORMATFILE = N'C: \ \ Format.txt Базы данных»,
файл ошибок = N'C: \ Temp \ MovieLensRatings.txt ') AS a;

Здесь главное выбрать столбцы для вставки.


Я получаю сообщение об ошибке 'Должен объявить переменную таблицы "@TempWithheldTable"
atreeon

-5

Одна из причин использования SELECT INTO заключается в том, что он позволяет использовать IDENTITY:

SELECT IDENTITY(INT,1,1) AS Id, name
INTO #MyTable 
FROM (SELECT name FROM AnotherTable) AS t

Это не будет работать с табличной переменной, что очень плохо ...


6
Вы можете объявить переменную таблицы с помощью IDENTITYстолбца.
Мартин Смит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.