3

First of all: this is not a kind of a IDENTITY() field.

In QlikView, it is used to generate a number based on parameters send to function. See its documentation here: https://help.qlik.com/en-US/qlikview/November2017/Subsystems/Client/Content/Scripting/CounterFunctions/autonumber.htm

In short, you send a parameter to it and it returns an integer that will identify the same arguments for the rest of script. If you send...

   AutoNumber('Name 900') -> returns 1
   AutoNumber('Name 300') -> returns 2
   AutoNumber('Name 001') -> returns 3
   AutoNumber('Name 900') -> returns 1 ... again 

and because the parameter is already in the intern list of AutoNumber

I tried to build some like that in SQL Server, but is not possible use SELECTs inside scalar functions.

My need is to get something like...

INSERT INTO FacSales (SumaryID, InvoiceID, InvoiceDate
                    , ProductID, SaleValue, CustomerID, VendorID)
SELECT AutoNumber(sale.VendorID, sale.CustomerID, sale.ProductID)
     , sale.InvoiceID
     , sale.SaleDate
     , details.ProductID
     , etc, etc, etc.

Is there, inside SQL Server, a "native" function that perform this? Or, is there a way to build this using a procedure/function?

Thanks.

4 Answers4

2

You could use DENSE_RANK (Transact-SQL)

Returns the rank of rows within the partition of a result set, without any gaps in the ranking. The rank of a row is one plus the number of distinct ranks that come before the row in question.

declare @T table
(
  ID int identity,
  VendorID int,
  CustomerID int,
  ProductID int
);

insert into @T values
(1, 2, 3),
(1, 2, 3),
(1, 2, 4),
(1, 2, 3);

select sale.ID,
       sale.VendorID,
       sale.CustomerID,
       sale.ProductID,
       dense_rank() over(order by sale.VendorID, 
                                  sale.CustomerID, 
                                  sale.ProductID) as AutoNumber
from @T as sale
order by sale.ID;

Result:

ID          VendorID    CustomerID  ProductID   AutoNumber
----------- ----------- ----------- ----------- --------------------
1           1           2           3           1
2           1           2           3           1
3           1           2           4           2
4           1           2           3           1
Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
  • Hi, Mikael. Very thanks...I didn't know this function. And it will be useful in other issues. But it can't help me in this case because the target table is INSERT by several sources. Imagine that I receive sales data from 7 sectors and a salesperson can act in more than one of them. In this way, is possible that my first set of data has 3,000 rows. After, I receive a set with 8,000 rows. In the result/target table parameters {vendor,customer,product} is ranked in 1,357 inside 3,000 lines at first time, but it will be not the same rank when compared for 8,000 lines. – Ricardo Ildefonso Mar 09 '18 at 14:34
  • May you sugest another way, Mikael? – Ricardo Ildefonso Mar 09 '18 at 14:41
  • @RicardoIldefonso You could create a table with an identity column as a primary key and all other columns as candidate key and use that table as a mapping between all columns used with an integer that represents the same data. There is of course extra work needed (more queries) by the server to maintain this table. – Mikael Eriksson Mar 09 '18 at 14:48
1

You basically want a key value store. There are lots of ways to make one.

Here is a possible solution. It uses a stored procedure.

However, you did not say if the values are retained indefinitely or if they are just for a single call. This example shows how to do it indefinitely.

It could be modified to be for a single call or connection via careful use of temporary tables. If it is other than a call or connection then the autoNumber.AutoNumber table and the autoNumber.NextAutoNumber will need to be cleaned up on what ever that schedule is.

-- Create the table, sequence and sproc
-- Create a schema to hold our autonumber table and sequence
CREATE SCHEMA autoNumber
GO

-- Create a sequence.  This just gives us a new number when ever we want.
-- This could be replaced with an identity column.
CREATE SEQUENCE autoNumber.NextAutoNumber AS [bigint]
 START WITH 1
 INCREMENT BY 1 
 NO CACHE 
GO

-- Create a table to hold the auto number key value pairs.
CREATE TABLE autoNumber.AutoNumber(KeyValue varchar(255), Number bigint)
go

-- This is the stored procedure that actually does the work of getting the autonumber
CREATE PROCEDURE autoNumber.GetAutoNumber @KeyValue varchar(255), @AutoNumber bigint = -1 output  AS
BEGIN
    DECLARE @Number bigint = null

    -- See if we already have an autonumber created for this keyvalue
    -- If we do, then set @Number to that value
    SELECT  @Number = autoNum.Number
    FROM    autoNumber.AutoNumber autoNum
    WHERE   autoNum.KeyValue = @KeyValue

    IF (@Number is null)
    BEGIN
        -- If @Number was not changed, then we did not find one
        -- in the table for this @KeyValue.  Make a new one
        -- and insert it.
        SET @Number = NEXT VALUE FOR autonumber.NextAutoNumber

        INSERT INTO autoNumber.AutoNumber ( KeyValue, Number)
        VALUES (@KeyValue, @Number)
    END

    -- Return our number to the caller.
    -- This uses either an output parameter or a select.
    IF (@AutoNumber = -1)
    BEGIN
        select @Number        
    END ELSE
    BEGIN
        set @AutoNumber = @Number    
    END
END
GO
-- End Create

-- Testing with "select"
EXEC autoNumber.GetAutoNumber 'Name 900'
EXEC autoNumber.GetAutoNumber 'Name 300'
EXEC autoNumber.GetAutoNumber 'Name 001'
EXEC autoNumber.GetAutoNumber 'Name 900'

-- Testing with output parameter
DECLARE @AutoNumber bigint
EXEC autoNumber.GetAutoNumber 'Name 900', @AutoNumber OUTPUT
SELECT @AutoNumber
EXEC autoNumber.GetAutoNumber 'Name 300', @AutoNumber OUTPUT
SELECT @AutoNumber
EXEC autoNumber.GetAutoNumber 'Name 001', @AutoNumber OUTPUT
SELECT @AutoNumber
EXEC autoNumber.GetAutoNumber 'Name 900', @AutoNumber OUTPUT
SELECT @AutoNumber

-- End Testing

-- Clean up 
DROP PROCEDURE autoNumber.GetAutoNumber
GO 

DROP TABLE autoNumber.AutoNumber
GO

drop SEQUENCE autoNumber.NextAutoNumber

DROP SCHEMA autoNumber
GO 
-- End Cleanup
Vaccano
  • 78,325
  • 149
  • 468
  • 850
  • Thank you, Vacano. The problem with this sugestion is that I need INSERT the records and its generated key while using a SELECT. – Ricardo Ildefonso Mar 09 '18 at 20:03
  • @RicardoIldefonso - are you limited to just one statement some how? Or could you do a setup statement first? (I think this could be done in two statements) – Vaccano Mar 10 '18 at 17:24
0

The closest SQL Server has is CHECKSUM function.

You can use it to calculate a hash value of any number of columns e.g.

SELECT CHECKSUM( 'abc', 123, 'zxc' )
UNION ALL
SELECT CHECKSUM( 'abc', 124, 'zxc' )
UNION ALL
SELECT CHECKSUM( 'abc', 123, 'zxc' )

Output:

-----------
53066784
53066832
53066784
Alex
  • 4,885
  • 3
  • 19
  • 39
-1

I think you are looking for ROW_NUMBER().

With this sql function you can partition and order by all the field you need.

SELECT ROW_NUMBER() OVER(PARTITION BY sale.VendorID, sale.CustomerID, sale.ProductID ORDER BY sale.VendorID, sale.CustomerID, sale.ProductID)
 , sale.InvoiceID
 , sale.SaleDate
 , details.ProductID FROM table
Alex Samson
  • 125
  • 2
  • 10
  • Hi, Alex. Thank you. For the same reason I wrote just above, this kind of rank can't be applied in this especific problem. Have you, please, another sugestion? – Ricardo Ildefonso Mar 09 '18 at 14:38
  • I think we need more details about how you plan to use this function. In your question you just posted a query which did lead me to this answer. Do you plan to have a stored procedure called by multiples programs that add data to the table? I also guess that the auto number will have to take into account the previous auto generated values ? – Alex Samson Mar 12 '18 at 12:36
  • Hi, Alex. I want a function that returns values to be insert in a table. GetAutonum('Ricardo', '555.444.333.22', 'Londrina', 'Brasil') ... See, every time a call the function with the same args it could return the same value to represent that set. In this way, I can generate strong keys to replace a long set of strings by a little integer. This function could be relationed with a table, where the args would be preserved. The goal is get a function to do two tasks: 1. search and find > return the existing ID; 2. search but don't find > INSERT the new set of args than return the new ID. – Ricardo Ildefonso Mar 14 '18 at 15:31
  • If I understand correctly what you want there is no native function in sql server to do so. To achieve what you wanna do you indeed will have to create a custom function and a table to hold data. Do you need that function to be usable in many contexts (with different parameter types) or will the parameters always be in the same order and types? – Alex Samson Mar 15 '18 at 14:09