Insert Data set into two tables and grabbing identity from one, for the other

  • Ok I think I will need to use a temp table for this and there is no code to share as of yet. Here is the intent.

    I need to insert data into two tables (a header and detail table) the Header Table will give me lets say an order number and this order number needs to be placed on the corresponding detail lines in the detail table.

    Now if I were inserting a single invoice with one or more detail lines EASY, just set @@Identity to a variable and do a second insert statement.

    What is happening is I will be importing a ton of Invoice headers and inserting those into the header table. The details are already in the database across various tables and and I will do that insert based on a select with some joins. As stated I need to get the invoice number from IDENTITY of the header table for each DETAIL insert.

    I am assuming the only way to do this is with a loop... Insert one header, get identity; Insert the detail table and include the IDENTITY variable, and repeat.

    Please let me know if there is a set-based approach to this.

    <hr noshade size=1 width=250 color=#BBC8E5> Regards,Jeffery Williams http://www.linkedin.com/in/jwilliamsoh

  • Jeffery Williams (1/2/2015)


    Ok I think I will need to use a temp table for this and there is no code to share as of yet. Here is the intent.

    I need to insert data into two tables (a header and detail table) the Header Table will give me lets say an order number and this order number needs to be placed on the corresponding detail lines in the detail table.

    Now if I were inserting a single invoice with one or more detail lines EASY, just set @@Identity to a variable and do a second insert statement.

    What is happening is I will be importing a ton of Invoice headers and inserting those into the header table. The details are already in the database across various tables and and I will do that insert based on a select with some joins. As stated I need to get the invoice number from IDENTITY of the header table for each DETAIL insert.

    I am assuming the only way to do this is with a loop... Insert one header, get identity; Insert the detail table and include the IDENTITY variable, and repeat.

    Please let me know if there is a set-based approach to this.

    Quick suggestion, use the output clause, consider the following sample code

    😎

    USE tempdb;

    SET NOCOUNT ON;

    GO

    IF OBJECT_ID('dbo.TBL_DETAIL') IS NOT NULL DROP TABLE dbo.TBL_DETAIL;

    IF OBJECT_ID('dbo.TBL_HEADER') IS NOT NULL DROP TABLE dbo.TBL_HEADER;

    IF OBJECT_ID('dbo.TBL_IMPORT') IS NOT NULL DROP TABLE dbo.TBL_IMPORT;

    /* The simplest of headers, the HEADER_INT */

    CREATE TABLE dbo.TBL_HEADER

    (

    HEADER_ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_INT INT NOT NULL

    );

    CREATE TABLE dbo.TBL_DETAIL

    (

    DETAIL_ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_ID INT NOT NULL CONSTRAINT FK_DBO_TBL_DETAIL_DBO_TBL_HEADER_HEADER_ID FOREIGN KEY REFERENCES dbo.TBL_HEADER(HEADER_ID)

    ,DETAIL_INT INT NOT NULL

    );

    /* RAW Document table */

    CREATE TABLE dbo.TBL_IMPORT

    (

    HEADER_INT INT NOT NULL

    ,DETAIL_INT INT NOT NULL

    );

    /* Sample "document" data*/

    INSERT INTO dbo.TBL_IMPORT (

    HEADER_INT

    ,DETAIL_INT

    )

    VALUES (1001,11001)

    ,(1001,11001)

    ,(1002,12002)

    ,(1002,12002)

    ,(1003,13003)

    ,(1003,13003)

    ,(1004,14004)

    ,(1004,14004)

    ,(1005,15005)

    ,(1005,15005);

    /* Table variable to store the identity values */

    DECLARE @HEADER_IDENTITY TABLE

    (

    HEADER_ID INT NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_INT INT NOT NULL

    );

    /* Insert the "document" HEADERS */

    INSERT INTO dbo.TBL_HEADER(HEADER_INT)

    OUTPUT inserted.HEADER_ID

    ,inserted.HEADER_INT

    INTO @HEADER_IDENTITY(HEADER_ID,HEADER_INT)

    SELECT DISTINCT SD.HEADER_INT

    FROM dbo.TBL_IMPORT SD

    /* Insert the DETAILs */

    INSERT INTO dbo.TBL_DETAIL (HEADER_ID,DETAIL_INT)

    SELECT

    HI.HEADER_ID

    ,TI.DETAIL_INT

    FROM dbo.TBL_IMPORT TI

    INNER JOIN @HEADER_IDENTITY HI

    ON TI.HEADER_INT = HI.HEADER_INT;

    SELECT

    TH.HEADER_ID AS HEADER_HEADER_ID

    ,TH.HEADER_INT AS HEADER_HEADER_INT

    ,DT.DETAIL_ID AS DETAIL_DETAIL_ID

    ,DT.HEADER_ID AS DETAIL_HEADER_ID

    ,DT.DETAIL_INT AS DETAIL_DETAIL_INT

    FROM dbo.TBL_HEADER TH

    INNER JOIN dbo.TBL_DETAIL DT

    ON TH.HEADER_ID = DT.HEADER_ID;

    Results

    HEADER_HEADER_ID HEADER_HEADER_INT DETAIL_DETAIL_ID DETAIL_HEADER_ID DETAIL_DETAIL_INT

    ---------------- ----------------- ---------------- ---------------- -----------------

    1 1001 1 1 11001

    1 1001 2 1 11001

    2 1002 3 2 12002

    2 1002 4 2 12002

    3 1003 5 3 13003

    3 1003 6 3 13003

    4 1004 7 4 14004

    4 1004 8 4 14004

    5 1005 9 5 15005

    5 1005 10 5 15005

  • Thank you, I will play with this. I have not used the OUTPUT clause yet.

    <hr noshade size=1 width=250 color=#BBC8E5> Regards,Jeffery Williams http://www.linkedin.com/in/jwilliamsoh

  • I could be wrong but that looks like it presupposes what the identities will be.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Does your source header data include a natural key which you could use for lookup purposes after writing to the parent table?

    The absence of evidence is not evidence of absence.
    Martin Rees

    You can lead a horse to water, but a pencil must be lead.
    Stan Laurel

  • By the way, this is not a great idea:

    just set @@Identity to a variable and do a second insert statement.

    Rather than @@Identity, you would be better off using Scope_Identity(). @@Identity is not limited by scope.

    The absence of evidence is not evidence of absence.
    Martin Rees

    You can lead a horse to water, but a pencil must be lead.
    Stan Laurel

  • Jeff Moden (1/2/2015)


    I could be wrong but that looks like it presupposes what the identities will be.

    It doesn't as the output clause retrieves the identity values generated in the first insert, in this case into a table variable, then uses the insert set's key values to join to the variable in the second insert for the identity values (foreign key in this case).

    😎

    The sample data is somewhat flawed but the method is good;-), below is a correction and slightly more details in the code's comments.

    USE tempdb;

    SET NOCOUNT ON;

    GO

    IF OBJECT_ID('dbo.TBL_DETAIL') IS NOT NULL DROP TABLE dbo.TBL_DETAIL;

    IF OBJECT_ID('dbo.TBL_HEADER') IS NOT NULL DROP TABLE dbo.TBL_HEADER;

    IF OBJECT_ID('dbo.TBL_IMPORT') IS NOT NULL DROP TABLE dbo.TBL_IMPORT;

    /* The simplest of headers, the HEADER_INT

    Identity seed and increment set to values

    geater than 1 for demonstration purposes

    */

    CREATE TABLE dbo.TBL_HEADER

    (

    HEADER_ID INT IDENTITY(12345,137) NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_INT INT NOT NULL

    );

    /* The detail table has a foreign key to the

    identity value of the header, again the

    identity seed and increment set to values

    geater than 1 for demonstration purposes

    */

    CREATE TABLE dbo.TBL_DETAIL

    (

    DETAIL_ID INT IDENTITY(54321,11) NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_ID INT NOT NULL CONSTRAINT FK_DBO_TBL_DETAIL_DBO_TBL_HEADER_HEADER_ID FOREIGN KEY REFERENCES dbo.TBL_HEADER(HEADER_ID)

    ,DETAIL_INT INT NOT NULL

    );

    /* RAW Document table */

    CREATE TABLE dbo.TBL_IMPORT

    (

    HEADER_INT INT NOT NULL

    ,DETAIL_INT INT NOT NULL

    );

    /* Sample "document" data, combination

    of header-detail numbers

    */

    INSERT INTO dbo.TBL_IMPORT (

    HEADER_INT

    ,DETAIL_INT

    )

    VALUES (1001,11011)

    ,(1001,11021)

    ,(1002,12032)

    ,(1002,12042)

    ,(1003,13053)

    ,(1003,13063)

    ,(1004,14074)

    ,(1004,14084)

    ,(1005,15095)

    ,(1005,15005);

    /* Table variable to temporarely store the identity values */

    DECLARE @HEADER_IDENTITY TABLE

    (

    HEADER_ID INT NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_INT INT NOT NULL

    );

    /* Insert the "document" HEADERS from the "import"

    table, the output clause then inserts the same

    and the identity values generated in the insert

    into the table variable

    */

    INSERT INTO dbo.TBL_HEADER(HEADER_INT)

    OUTPUT inserted.HEADER_ID

    ,inserted.HEADER_INT

    INTO @HEADER_IDENTITY(HEADER_ID,HEADER_INT)

    /* In the sample data, each header has one or more

    details, only the distinct set is needed for

    the insert clause

    */

    SELECT DISTINCT SD.HEADER_INT

    FROM dbo.TBL_IMPORT SD

    /* Insert the data into the DETAILs, joining the

    table variable to get the identity values from

    the header table

    */

    INSERT INTO dbo.TBL_DETAIL (HEADER_ID,DETAIL_INT)

    SELECT

    HI.HEADER_ID

    ,TI.DETAIL_INT

    FROM dbo.TBL_IMPORT TI

    INNER JOIN @HEADER_IDENTITY HI

    ON TI.HEADER_INT = HI.HEADER_INT;

    /* Verifying the results:

    1. Record count

    2. Value Pairs

    */

    SELECT

    TH.HEADER_ID AS HEADER_HEADER_ID

    ,TH.HEADER_INT AS HEADER_HEADER_INT

    ,DT.DETAIL_ID AS DETAIL_DETAIL_ID

    ,DT.HEADER_ID AS DETAIL_HEADER_ID

    ,DT.DETAIL_INT AS DETAIL_DETAIL_INT

    ,IP.HEADER_INT AS IMPORT_HEADER_INT

    ,IP.DETAIL_INT AS IMPORT_DETAIL_INT

    FROM dbo.TBL_HEADER TH

    INNER JOIN dbo.TBL_DETAIL DT

    ON TH.HEADER_ID = DT.HEADER_ID

    RIGHT OUTER JOIN dbo.TBL_IMPORT IP

    ON DT.DETAIL_INT = IP.DETAIL_INT;

    Results

    HEADER_HEADER_ID HEADER_HEADER_INT DETAIL_DETAIL_ID DETAIL_HEADER_ID DETAIL_DETAIL_INT IMPORT_HEADER_INT IMPORT_DETAIL_INT

    ---------------- ----------------- ---------------- ---------------- ----------------- ----------------- -----------------

    12345 1001 54321 12345 11011 1001 11011

    12345 1001 54332 12345 11021 1001 11021

    12482 1002 54343 12482 12032 1002 12032

    12482 1002 54354 12482 12042 1002 12042

    12619 1003 54365 12619 13053 1003 13053

    12619 1003 54376 12619 13063 1003 13063

    12756 1004 54387 12756 14074 1004 14074

    12756 1004 54398 12756 14084 1004 14084

    12893 1005 54409 12893 15095 1005 15095

    12893 1005 54420 12893 15005 1005 15005

  • Phil Parkin (1/3/2015)


    By the way, this is not a great idea:

    just set @@Identity to a variable and do a second insert statement.

    Rather than @@Identity, you would be better off using Scope_Identity(). @@Identity is not limited by scope.

    Quick thought, the identity type functions are not suitable for a set based approach as their results are limited to a single value forcing a RBAR approach.

    😎

    From BOL:

    IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.

    @@IDENTITY returns the last identity value generated for any table in the current session, across all scopes.

    SCOPE_IDENTITY returns the last identity value generated for any table in the current session and the current scope.

  • Eirikur Eiriksson (1/3/2015)


    Phil Parkin (1/3/2015)


    By the way, this is not a great idea:

    just set @@Identity to a variable and do a second insert statement.

    Rather than @@Identity, you would be better off using Scope_Identity(). @@Identity is not limited by scope.

    Quick thought, the identity type functions are not suitable for a set based approach as their results are limited to a single value forcing a RBAR approach.

    😎

    From BOL:

    IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.

    @@IDENTITY returns the last identity value generated for any table in the current session, across all scopes.

    SCOPE_IDENTITY returns the last identity value generated for any table in the current session and the current scope.

    Thanks, but I know this 🙂

    The quote was from the original post & I just wanted to throw this in as an aside, not as a suggested solution in this case.

    The absence of evidence is not evidence of absence.
    Martin Rees

    You can lead a horse to water, but a pencil must be lead.
    Stan Laurel

  • Phil Parkin (1/3/2015)


    Thanks, but I know this 🙂

    The quote was from the original post & I just wanted to throw this in as an aside, not as a suggested solution in this case.

    Was quite certain you did:-D, meant my input for completeness and to empathize on the set based approach.

    😎

  • Eirikur Eiriksson (1/3/2015)


    Jeff Moden (1/2/2015)


    I could be wrong but that looks like it presupposes what the identities will be.

    It doesn't as the output clause retrieves the identity values generated in the first insert, in this case into a table variable, then uses the insert set's key values to join to the variable in the second insert for the identity values (foreign key in this case).

    😎

    The sample data is somewhat flawed but the method is good;-), below is a correction and slightly more details in the code's comments.

    USE tempdb;

    SET NOCOUNT ON;

    GO

    IF OBJECT_ID('dbo.TBL_DETAIL') IS NOT NULL DROP TABLE dbo.TBL_DETAIL;

    IF OBJECT_ID('dbo.TBL_HEADER') IS NOT NULL DROP TABLE dbo.TBL_HEADER;

    IF OBJECT_ID('dbo.TBL_IMPORT') IS NOT NULL DROP TABLE dbo.TBL_IMPORT;

    /* The simplest of headers, the HEADER_INT

    Identity seed and increment set to values

    geater than 1 for demonstration purposes

    */

    CREATE TABLE dbo.TBL_HEADER

    (

    HEADER_ID INT IDENTITY(12345,137) NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_INT INT NOT NULL

    );

    /* The detail table has a foreign key to the

    identity value of the header, again the

    identity seed and increment set to values

    geater than 1 for demonstration purposes

    */

    CREATE TABLE dbo.TBL_DETAIL

    (

    DETAIL_ID INT IDENTITY(54321,11) NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_ID INT NOT NULL CONSTRAINT FK_DBO_TBL_DETAIL_DBO_TBL_HEADER_HEADER_ID FOREIGN KEY REFERENCES dbo.TBL_HEADER(HEADER_ID)

    ,DETAIL_INT INT NOT NULL

    );

    /* RAW Document table */

    CREATE TABLE dbo.TBL_IMPORT

    (

    HEADER_INT INT NOT NULL

    ,DETAIL_INT INT NOT NULL

    );

    /* Sample "document" data, combination

    of header-detail numbers

    */

    INSERT INTO dbo.TBL_IMPORT (

    HEADER_INT

    ,DETAIL_INT

    )

    VALUES (1001,11011)

    ,(1001,11021)

    ,(1002,12032)

    ,(1002,12042)

    ,(1003,13053)

    ,(1003,13063)

    ,(1004,14074)

    ,(1004,14084)

    ,(1005,15095)

    ,(1005,15005);

    /* Table variable to temporarely store the identity values */

    DECLARE @HEADER_IDENTITY TABLE

    (

    HEADER_ID INT NOT NULL PRIMARY KEY CLUSTERED

    ,HEADER_INT INT NOT NULL

    );

    /* Insert the "document" HEADERS from the "import"

    table, the output clause then inserts the same

    and the identity values generated in the insert

    into the table variable

    */

    INSERT INTO dbo.TBL_HEADER(HEADER_INT)

    OUTPUT inserted.HEADER_ID

    ,inserted.HEADER_INT

    INTO @HEADER_IDENTITY(HEADER_ID,HEADER_INT)

    /* In the sample data, each header has one or more

    details, only the distinct set is needed for

    the insert clause

    */

    SELECT DISTINCT SD.HEADER_INT

    FROM dbo.TBL_IMPORT SD

    /* Insert the data into the DETAILs, joining the

    table variable to get the identity values from

    the header table

    */

    INSERT INTO dbo.TBL_DETAIL (HEADER_ID,DETAIL_INT)

    SELECT

    HI.HEADER_ID

    ,TI.DETAIL_INT

    FROM dbo.TBL_IMPORT TI

    INNER JOIN @HEADER_IDENTITY HI

    ON TI.HEADER_INT = HI.HEADER_INT;

    /* Verifying the results:

    1. Record count

    2. Value Pairs

    */

    SELECT

    TH.HEADER_ID AS HEADER_HEADER_ID

    ,TH.HEADER_INT AS HEADER_HEADER_INT

    ,DT.DETAIL_ID AS DETAIL_DETAIL_ID

    ,DT.HEADER_ID AS DETAIL_HEADER_ID

    ,DT.DETAIL_INT AS DETAIL_DETAIL_INT

    ,IP.HEADER_INT AS IMPORT_HEADER_INT

    ,IP.DETAIL_INT AS IMPORT_DETAIL_INT

    FROM dbo.TBL_HEADER TH

    INNER JOIN dbo.TBL_DETAIL DT

    ON TH.HEADER_ID = DT.HEADER_ID

    RIGHT OUTER JOIN dbo.TBL_IMPORT IP

    ON DT.DETAIL_INT = IP.DETAIL_INT;

    Results

    HEADER_HEADER_ID HEADER_HEADER_INT DETAIL_DETAIL_ID DETAIL_HEADER_ID DETAIL_DETAIL_INT IMPORT_HEADER_INT IMPORT_DETAIL_INT

    ---------------- ----------------- ---------------- ---------------- ----------------- ----------------- -----------------

    12345 1001 54321 12345 11011 1001 11011

    12345 1001 54332 12345 11021 1001 11021

    12482 1002 54343 12482 12032 1002 12032

    12482 1002 54354 12482 12042 1002 12042

    12619 1003 54365 12619 13053 1003 13053

    12619 1003 54376 12619 13063 1003 13063

    12756 1004 54387 12756 14074 1004 14074

    12756 1004 54398 12756 14084 1004 14084

    12893 1005 54409 12893 15095 1005 15095

    12893 1005 54420 12893 15005 1005 15005

    Apologies, I misspoke. I'm not talking about the actual identities. I'm talking about the pre-established relationship of header to detail row afforded by the following in your good code.

    /* Sample "document" data, combination

    of header-detail numbers

    */

    INSERT INTO dbo.TBL_IMPORT (

    HEADER_INT

    ,DETAIL_INT

    )

    VALUES (1001,11011)

    ,(1001,11021)

    ,(1002,12032)

    ,(1002,12042)

    ,(1003,13053)

    ,(1003,13063)

    ,(1004,14074)

    ,(1004,14084)

    ,(1005,15095)

    ,(1005,15005);

    I'll see if I can come up with a simplified example similar to the data I've had to deal with in the past makes it impossible to do the classic Invoice/Invoice detail problem without the introduction of a special column in the header table.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff Moden (1/3/2015)


    I'll see if I can come up with a simplified example similar to the data I've had to deal with in the past makes it impossible to do the classic Invoice/Invoice detail problem without the introduction of a special column in the header table.

    I would be very grateful if you did, would like to compile some real world samples/solutions.

    😎

Viewing 12 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic. Login to reply