• herkusg - Wednesday, February 21, 2018 4:26 PM

    Hello all,

    I was recently assigned to convert an Access database to Sql Server.  I have to try and keep the structure the closest properly because some other programs use this database.

    This concrete table got a composite key for Company, Warehouse, ManufacturingID but the ID part starts over with each combination of Company and warehosue.

    Example:
    Company | Warehouse | ManufacturingID | ...
    AU          |  w01           |  1
    AU          |  w01           |  2
    AU          |  w04           |  1
    AU          |  w04           |  2
    NZ          |  w01           |  1
    NZ          |  w02           |  2

    The access program does this manually which I don't want to replicate because it would just cause troubles..

    I am looking for options on how to do this, preferably with not having to change much of the table so the other programs would be easy to adapt.

    Reading around a bit I think one possible solution could be to use a INSTEAD OF INSERT trigger, is this right?

    Thanks for any help.

    One way to achieve this is to use a "Tracking Table" where you have a row for each Company-Warehouse combination, and keep a record of the last used number.
    Use a proc to update the number, and return the number to the calling process.
    NOTE:  This can lead to hot-spotting.  However, since you are using Access, I assume that the volume will be low, and you should be OK.
    CREATE TABLE dbo.NextID (
      Company CHAR(2) NOT NULL
    , Warehouse CHAR(3) NOT NULL
    , NextID  INT  NOT NULL
    , CONSTRAINT PK_NextID PRIMARY KEY CLUSTERED(Company, Warehouse)
    );
    GO

    CREATE PROCEDURE dbo.GetNextID
      @Company CHAR(2)
    , @Warehouse CHAR(3)
    , @NextID  INT  OUTPUT
    AS
    BEGIN
    -- Ensure that there is a record for the provided Company-Warehouse combination
    INSERT INTO dbo.NextID (Company, Warehouse, NextID)
    SELECT @Company, @Warehouse, 0
    WHERE NOT EXISTS (SELECT 1 FROM dbo.NextID AS n WITH (XLOCK, HOLDLOCK)
           WHERE n.Company = @Company
            AND n.Warehouse = @Warehouse);

    -- Use a quirky Update to get the next ID
    UPDATE dbo.NextID
    SET @NextID = NextID = NextID + 1
    WHERE Company = @Company
      AND Warehouse = @Warehouse;

    END;
    GO

    DECLARE @Company CHAR(2) = 'AU';
    DECLARE @Warehouse CHAR(3) = 'w01';
    DECLARE @NextID  INT;

    EXEC dbo.GetNextID
      @Company = @Company
    , @Warehouse = @Warehouse
    , @NextID  = @NextID OUTPUT;

    SELECT
      Company = @Company
    , Warehouse = @Warehouse
    , NextID  = @NextID;
    GO

    Since this is a SQL 2014 board, I am assuming that you are on SQL 2014.
    In that case, an alternate method is to dynamically create a sequence object for each Company-Warehouse combination.
    You also create a proc to get the next number, but there is no tracking table.
    CREATE PROCEDURE dbo.GetNextSEQ
      @Company CHAR(2)
    , @Warehouse CHAR(3)
    , @NextID  INT  OUTPUT
    AS
    BEGIN
    DECLARE @SQL  NVARCHAR(4000);
    DECLARE @SeqName SYSNAME = N'dbo.' + @Company + '_' + @Warehouse + '_SEQ' ;

    -- Ensure that there is a dedicated sequence object for provided Company-Warehouse combination
    SET @SQL = N'IF OBJECT_ID(N''' + @SeqName + N''', N''SO'') IS NULL
    BEGIN
      CREATE SEQUENCE ' + @SeqName + N'
      AS INT
      MINVALUE  1
      START WITH  1
      INCREMENT BY 1
      NO CYCLE;
    END;';

    EXEC sys.sp_executesql @stmt = @SQL;

    SET @SQL = N'SET @NextID = NEXT VALUE FOR ' + @SeqName + N';';

    EXEC sys.sp_executesql
      @stmt = @SQL
      , @params = N'@NextID INT OUTPUT'
      , @NextID = @NextID OUTPUT;

    END;
    GO

    DECLARE @Company CHAR(2) = 'AU';
    DECLARE @Warehouse CHAR(3) = 'w01';
    DECLARE @NextID  INT;

    EXEC dbo.GetNextSEQ
      @Company = @Company
    , @Warehouse = @Warehouse
    , @NextID  = @NextID OUTPUT;

    SELECT
      Company = @Company
    , Warehouse = @Warehouse
    , NextID  = @NextID;
    GO

    SELECT *
    FROM sys.sequences AS s;