2015-07-30 6 views
11

Краткое объяснение на соответствующей части домена:Вставка в несколько таблиц

A Категория состоит из четырех данных:

  1. Пол (мужской/женский)
  2. Возраст Дивизии (Могучий клеща в магистратуру)
  3. Цвет пояса (от белого к черному)
  4. весе (Петух Плотная)

Итак, Male Adult Black Rooster образует одну категорию. Некоторые комбинации могут не существовать, например, могучий клещ черный пояс.

Спортсмен борется с спортсменами той же категории, и если он классифицирует, он борется с спортсменами разных весовых дивизий (но одного пола, возраста и пояса).

К моделированию. У меня есть таблица Category, уже заполненная всеми комбинациями, существующими в домене.

CREATE TABLE Category (
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [AgeDivision_Id] [int] NULL, 
    [Gender] [int] NULL, 
    [BeltColor] [int] NULL, 
    [WeightDivision] [int] NULL 
) 

CategorySet и CategorySet_Category, которая образует многие ко многим с Category.

CREATE TABLE CategorySet (
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [Championship_Id] [int] NOT NULL, 
) 

CREATE TABLE CategorySet_Category (
    [CategorySet_Id] [int] NOT NULL, 
    [Category_Id] [int] NOT NULL 
) 

Учитывая следующий набор результатов:

| Options_Id | Championship_Id | AgeDivision_Id | BeltColor | Gender | WeightDivision | 
    |------------|-----------------|----------------|-----------|--------|----------------| 
1. | 2963  | 422    | 15    | 7   | 0  | 0    | 
2. | 2963  | 422    | 15    | 7   | 0  | 1    | 
3. | 2963  | 422    | 15    | 7   | 0  | 2    | 
4. | 2963  | 422    | 15    | 7   | 0  | 3    | 
5. | 2964  | 422    | 15    | 8   | 0  | 0    | 
6. | 2964  | 422    | 15    | 8   | 0  | 1    | 
7. | 2964  | 422    | 15    | 8   | 0  | 2    | 
8. | 2964  | 422    | 15    | 8   | 0  | 3    | 

Поскольку спортсмены могут бороться два CategorySets, мне нужно CategorySet и CategorySet_Category быть заполнены двумя разными способами (это может быть два запроса):

Один Category_Set для каждого ряда, с одним CategorySet_Category, указывая на соответствующий Category.

Один Category_Set, который группирует все WeightDivisions одним CategorySet в том же AgeDivision_Id, BeltColor, Gender. В этом примере изменяется только BeltColor.

Таким образом, окончательный результат будет иметь в общей сложности 10 CategorySet строк:

| Id | Championship_Id | 
|----|-----------------| 
| 1 | 422    | 
| 2 | 422    | 
| 3 | 422    | 
| 4 | 422    | 
| 5 | 422    | 
| 6 | 422    | 
| 7 | 422    | 
| 8 | 422    | 
| 9 | 422    | /* groups different Weight Division for BeltColor 7 */ 
| 10 | 422    | /* groups different Weight Division for BeltColor 8 */ 

И CategorySet_Category будет иметь 16 строк:

| CategorySet_Id | Category_Id | 
|----------------|-------------| 
| 1    | 1   | 
| 2    | 2   | 
| 3    | 3   | 
| 4    | 4   | 
| 5    | 5   | 
| 6    | 6   | 
| 7    | 7   | 
| 8    | 8   | 
| 9    | 1   | /* groups different Weight Division for BeltColor 7 */ 
| 9    | 2   | /* groups different Weight Division for BeltColor 7 */ 
| 9    | 3   | /* groups different Weight Division for BeltColor 7 */ 
| 9    | 4   | /* groups different Weight Division for BeltColor 7 */ 
| 10    | 5   | /* groups different Weight Division for BeltColor 8 */ 
| 10    | 6   | /* groups different Weight Division for BeltColor 8 */ 
| 10    | 7   | /* groups different Weight Division for BeltColor 8 */ 
| 10    | 8   | /* groups different Weight Division for BeltColor 8 */ 

Я понятия не имею, как вставить в CategorySet, захватить его сгенерированный Id, затем используйте его для вставки в CategorySet_Category

Надеюсь, я сделал свои намерения ясными ,

У меня также created a SQLFiddle.

Редактировать 1: Я прокомментировал ответ Яцека, что это будет работать только один раз, но это неверно. Он будет работать пару раз в неделю. У меня есть возможность запускаться как команда SQL из C# или хранимой процедуры. Производительность не имеет решающего значения.

Редактировать 2: Jacek предложил использовать SCOPE_IDENTITY, чтобы вернуть Id. Проблема в том, что SCOPE_IDENTITY возвращает только последний вставленный идентификатор, и я вставляю несколько строк в CategorySet.

Редактировать 3: Ответа на этот вопрос @FutbolFan, который спрашивает, как получить FakeResultSet.

Это таблица CategoriesOption (Id, Price_Id, MaxAthletesByTeam)

И столы CategoriesOptionBeltColor, CategoriesOptionAgeDivision, CategoriesOptionWeightDivison, CategoriesOptionGender. Эти четыре таблицы в основном одинаковы (Id, CategoriesOption_Id, Value).

Внешний вид запроса, как это:

SELECT * FROM CategoriesOption co 
LEFT JOIN CategoriesOptionAgeDivision ON 
    CategoriesOptionAgeDivision.CategoriesOption_Id = co.Id 
LEFT JOIN CategoriesOptionBeltColor ON 
    CategoriesOptionBeltColor.CategoriesOption_Id = co.Id 
LEFT JOIN CategoriesOptionGender ON 
    CategoriesOptionGender.CategoriesOption_Id = co.Id 
LEFT JOIN CategoriesOptionWeightDivision ON 
    CategoriesOptionWeightDivision.CategoriesOption_Id = co.Id 
+0

Один вопрос: Каким образом у вас есть только 8 строк в вашем файле 'fakeresultset' и ожидает 10 различных строк в вашем файле' CategorySet' таблицы? Я не мог полностью следовать логике этого. – FutbolFan

+0

@FutbolFan Как я уже сказал, это могут быть два запроса. 1 'CategorySet' и 1' CategorySet_Category' для каждой строки в поддельном наборе результатов, всего 8 строк. И 1 'CategorySet' для каждого BeltColor, делая еще два из них и еще 8 строк' CategorySet_Category' – Andre

+0

@FutbolFan Возможно, я могу написать второй, если первый предоставлен, несмотря на отсутствие навыков в SQL – Andre

ответ

2

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

Первая часть является прямой вперед

(AB) использовать MERGE с OUTPUT п.

MERGE май INSERT, UPDATE и DELETE строки. В нашем случае нам нужно только INSERT. 1=0 всегда неверно, поэтому часть NOT MATCHED BY TARGET всегда выполняется. В общем, могут быть другие отрасли, см. Документы. WHEN MATCHED обычно используется для UPDATE; WHEN NOT MATCHED BY SOURCE обычно используется для DELETE, но мы не нуждаемся в них здесь.

Эта запутанная форма MERGE эквивалентна простой INSERT, но в отличие от простой INSERT его положение OUTPUT позволяет ссылаться на столбцы, которые нам нужны.

MERGE INTO CategorySet 
USING 
(
    SELECT 
     FakeResultSet.Championship_Id 
     ,FakeResultSet.Price_Id 
     ,FakeResultSet.MaxAthletesByTeam 
     ,Category.Id AS Category_Id 
    FROM 
     FakeResultSet 
     INNER JOIN Category ON 
      Category.AgeDivision_Id = FakeResultSet.AgeDivision_Id AND 
      Category.Gender = FakeResultSet.Gender AND 
      Category.BeltColor = FakeResultSet.BeltColor AND 
      Category.WeightDivision = FakeResultSet.WeightDivision 
) AS Src 
ON 1 = 0 
WHEN NOT MATCHED BY TARGET THEN 
INSERT 
    (Championship_Id 
    ,Price_Id 
    ,MaxAthletesByTeam) 
VALUES 
    (Src.Championship_Id 
    ,Src.Price_Id 
    ,Src.MaxAthletesByTeam) 
OUTPUT inserted.id AS CategorySet_Id, Src.Category_Id 
INTO CategorySet_Category (CategorySet_Id, Category_Id) 
; 

FakeResultSet соединяется с Category, чтобы получить Category.id для каждой строки FakeResultSet. Предполагается, что Category имеет уникальные комбинации AgeDivision_Id, Gender, BeltColor, WeightDivision.

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

Запрос MERGE должен вставить 8 строк в CategorySet и вставить 8 строк в CategorySet_Category с использованием сгенерированных идентификаторов.

Вторая часть

необходимо временную таблицу. Я буду использовать переменную таблицы для хранения сгенерированных идентификаторов.

DECLARE @T TABLE (
    CategorySet_Id int 
    ,AgeDivision_Id int 
    ,Gender int 
    ,BeltColor int); 

Мы должны помнить, сгенерированный CategorySet_Id вместе с комбинацией AgeDivision_Id, Gender, BeltColor, вызвавшей его.

MERGE INTO CategorySet 
USING 
(
    SELECT 
     FakeResultSet.Championship_Id 
     ,FakeResultSet.Price_Id 
     ,FakeResultSet.MaxAthletesByTeam 
     ,FakeResultSet.AgeDivision_Id 
     ,FakeResultSet.Gender 
     ,FakeResultSet.BeltColor 
    FROM 
     FakeResultSet 
    GROUP BY 
     FakeResultSet.Championship_Id 
     ,FakeResultSet.Price_Id 
     ,FakeResultSet.MaxAthletesByTeam 
     ,FakeResultSet.AgeDivision_Id 
     ,FakeResultSet.Gender 
     ,FakeResultSet.BeltColor 
) AS Src 
ON 1 = 0 
WHEN NOT MATCHED BY TARGET THEN 
INSERT 
    (Championship_Id 
    ,Price_Id 
    ,MaxAthletesByTeam) 
VALUES 
    (Src.Championship_Id 
    ,Src.Price_Id 
    ,Src.MaxAthletesByTeam) 
OUTPUT 
    inserted.id AS CategorySet_Id 
    ,Src.AgeDivision_Id 
    ,Src.Gender 
    ,Src.BeltColor 
INTO @T(CategorySet_Id, AgeDivision_Id, Gender, BeltColor) 
; 

MERGE выше, будет группа FakeResultSet по мере необходимости и вставить 2 строки в CategorySet и 2 строк в @T.

Тогда присоединяйтесь @T с Category получить Category.IDs:

INSERT INTO CategorySet_Category (CategorySet_Id, Category_Id) 
SELECT 
    TT.CategorySet_Id 
    ,Category.Id AS Category_Id 
FROM 
    @T AS TT 
    INNER JOIN Category ON 
     Category.AgeDivision_Id = TT.AgeDivision_Id AND 
     Category.Gender = TT.Gender AND 
     Category.BeltColor = TT.BeltColor 
; 

Это будет вставить 8 строк в CategorySet_Category.

+0

Одна вещь, которую я не понимал, для чего 1 = 0? – Andre

+1

['MERGE'] (https://msdn.microsoft.com/en-us/library/bb510625.aspx) может содержать строки' INSERT', 'UPDATE' и' DELETE'. В нашем случае нам нужно только «INSERT». '1 = 0' всегда false, поэтому часть' NOT MATCHED BY TARGET' всегда выполняется. В общем, могут быть и другие ветви, но мы здесь не нужны. Эта запутанная форма 'MERGE' эквивалентна простому' INSERT', но в отличие от простого 'INSERT' предложение' OUTPUT' позволяет ссылаться на нужные столбцы. –

0

@@ IDENTITY является вашим другом на 2-й части вопроса https://msdn.microsoft.com/en-us/library/ms187342.aspx и Best way to get identity of inserted row?

Некоторые API (драйверы) возвращает Int из обновлений() функция, то есть идентификатор, если он «вставлен». Какой API/среда вы используете?

Непонятная проблема 1. Вы не должны вставлять столбец идентификатора.

+0

Но как мне получить правильный идентификатор? Если я вставляю две строки и 'выбираю SCOPE_IDENTITY()', он будет возвращать последний сгенерированный, 2. Во второй таблице мне нужно вставить данные как для 1, так и для 2. – Andre

+0

Это одноразовый сценарий импорта и будет выполненная студия MSSQL – Andre

+2

, вы используете предложение OUTPUT. – HLGEM

1

Вот не полный ответ, но направление, которое вы можете использовать, чтобы решить эту проблему:

первый запрос:

select row_number() over(order by t, Id) as n, Championship_Id 
from (
select distinct 0 as t, b.Id, a.Championship_Id 
from FakeResultSet as a 
inner join 
Category as b 
on 
a.AgeDivision_Id=b.AgeDivision_Id and 
a.Gender=b.Gender and 
a.BeltColor=b.BeltColor and 
a.WeightDivision=b.WeightDivision 
union all 
select distinct 1, BeltColor, Championship_Id 
from FakeResultSet 
) as q 

второй запрос:

select q2.CategorySet_Id, c.Id as Category_Id from (
select row_number() over(order by t, Id) as CategorySet_Id, Id, BeltColor 
from (
    select distinct 0 as t, b.Id, null as BeltColor 
    from FakeResultSet as a 
    inner join 
    Category as b 
    on 
    a.AgeDivision_Id=b.AgeDivision_Id and 
    a.Gender=b.Gender and 
    a.BeltColor=b.BeltColor and 
    a.WeightDivision=b.WeightDivision 
    union all 
    select distinct 1, BeltColor, BeltColor 
    from FakeResultSet 
) as q 
) as q2 
inner join 
Category as c 
on 
(q2.BeltColor is null and q2.Id=c.Id) 
OR 
(q2.BeltColor = c.BeltColor) 

, конечно, это будет работать только для пустых CategorySet и CategorySet_Category таблиц, но вы можете использовать select coalese(max(Id), 0) from CategorySet для получения текущего номера и добавления его в row_number, таким образом, вы будете получить реальный идентификатор, который будет вставлен в строку CategorySet для второго запроса

1

Что я делаю, когда сталкиваюсь с этими ситуациями, является создание одной или нескольких временных таблиц с помощью row_number() над предложениями, дающими мне идентификаторы во временных таблицах. Затем я проверяю наличие каждой записи в фактических таблицах, и если они существуют, обновите временную таблицу с фактическими идентификаторами записей. Наконец, я запустил цикл while во временных таблицах, в которых отсутствует фактический идентификатор и вставляем их по одному, после вставки я обновляю временную запись таблицы с фактическими идентификаторами. Это позволяет вам контролировать все данные контролируемым образом.

0

Ниже запроса даст конечный результат Для строк CategorySet:

SELECT 
     ROW_NUMBER() OVER (PARTITION BY Championship_Id ORDER BY Championship_Id) RNK, 
     Championship_Id 

FROM 
(
    SELECT 
      Championship_Id 
      ,BeltColor 
    FROM #FakeResultSet 
    UNION ALL 
    SELECT 
      Championship_Id,BeltColor 
    FROM #FakeResultSet 
    GROUP BY Championship_Id,BeltColor 
)BASE  
Смежные вопросы