Существует ли функция SQL Server, эквивалентная AutoNumber() в QlikView?

Прежде всего: это не поле IDENTITY().

В QlikView он используется для генерации числа на основе параметров, отправленных в функцию. См. его документацию здесь: https://help.qlik.com/en-US/qlikview/November2017/Subsystems/Client/Content/Scripting/CounterFunctions/autonumber.htm

Короче говоря, вы отправляете ему параметр, и он возвращает целое число, которое будет идентифицировать те же аргументы для остальной части скрипта. Если отправить...

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

и потому что параметр уже находится во внутреннем списке автонумерации

Я пытался создать что-то подобное в SQL Server, но невозможно использовать SELECT внутри скалярных функций.

Мне нужно получить что-то вроде...

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.

Есть ли внутри SQL Server «собственная» функция, которая выполняет это? Или есть способ построить это с помощью процедуры/функции?

Спасибо.


person Ricardo Ildefonso    schedule 09.03.2018    source источник


Ответы (4)


Вы можете использовать DENSE_RANK (Transact-SQL )

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

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;

Результат:

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
person Mikael Eriksson    schedule 09.03.2018
comment
Привет, Микаэль. Большое спасибо... Я не знал эту функцию. Да и в других вопросах пригодится. Но в данном случае это мне не поможет, потому что целевая таблица INSERT из нескольких источников. Представьте, что я получаю данные о продажах из 7 секторов, и продавец может действовать более чем в одном из них. Таким образом, возможно, что мой первый набор данных содержит 3000 строк. После этого я получаю набор из 8000 строк. В таблице результатов/целей параметры {vendor,customer,product} занимают 1357 место в 3000 строк в первый раз, но это будет другое место при сравнении 8000 строк. - person Ricardo Ildefonso; 09.03.2018
comment
Не могли бы вы предложить другой способ, Микаэль? - person Ricardo Ildefonso; 09.03.2018
comment
@RicardoIldefonso Вы можете создать таблицу со столбцом идентификаторов в качестве первичного ключа и всеми остальными столбцами в качестве ключа-кандидата и использовать эту таблицу в качестве сопоставления между всеми столбцами, используемыми с целым числом, представляющим одни и те же данные. Конечно, серверу требуется дополнительная работа (больше запросов) для обслуживания этой таблицы. - person Mikael Eriksson; 09.03.2018

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

Вот возможное решение. Он использует хранимую процедуру.

Однако вы не сказали, сохраняются ли значения на неопределенный срок или они предназначены только для одного вызова. Этот пример показывает, как сделать это бесконечно.

Его можно изменить для одного вызова или соединения путем осторожного использования временных таблиц. Если это не вызов или соединение, то таблицы autoNumber.AutoNumber и autoNumber.NextAutoNumber должны быть очищены по какому-либо расписанию.

-- 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
person Vaccano    schedule 09.03.2018
comment
Спасибо, Вакано. Проблема с этим предложением заключается в том, что мне нужно ВСТАВИТЬ записи и их сгенерированный ключ при использовании SELECT. - person Ricardo Ildefonso; 09.03.2018
comment
@RicardoIldefonso - вы каким-то образом ограничены одним утверждением? Или вы могли бы сначала сделать оператор установки? (Я думаю, что это можно было бы сделать в двух утверждениях) - person Vaccano; 10.03.2018

Ближайший SQL Server — CHECKSUM.

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

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

Выход:

-----------
53066784
53066832
53066784
person Alex    schedule 29.10.2019

Я думаю, вы ищете ROW_NUMBER( ).

С помощью этой функции sql вы можете разделить и упорядочить все поля, которые вам нужны.

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
person Alex Samson    schedule 09.03.2018
comment
Привет, Алекс. Спасибо. По той же причине, о которой я писал чуть выше, такой ранг неприменим в данной конкретной задаче. У вас есть, пожалуйста, другое предложение? - person Ricardo Ildefonso; 09.03.2018
comment
Думаю, нам нужно больше подробностей о том, как вы планируете использовать эту функцию. В своем вопросе вы только что разместили запрос, который привел меня к этому ответу. Планируете ли вы иметь хранимую процедуру, вызываемую несколькими программами, которые добавляют данные в таблицу? Я также предполагаю, что автоматический номер должен будет учитывать предыдущие автоматически сгенерированные значения? - person Alex Samson; 12.03.2018
comment
Привет, Алекс. Мне нужна функция, которая возвращает значения для вставки в таблицу. GetAutonum('Рикардо', '555.444.333.22', 'Лондрина', 'Бразилия') ... Видите ли, каждый раз, когда функция вызывается с одними и теми же аргументами, она может возвращать одно и то же значение для представления этого набора. Таким образом, я могу генерировать надежные ключи для замены длинного набора строк небольшим целым числом. Эта функция может быть связана с таблицей, в которой будут сохранены аргументы. Цель состоит в том, чтобы получить функцию, выполняющую две задачи: 1. искать и находить › возвращать существующий идентификатор; 2. искать, но не найти › ВСТАВИТЬ новый набор аргументов, чем вернуть новый идентификатор. - person Ricardo Ildefonso; 14.03.2018
comment
Если я правильно понимаю, что вы хотите, на сервере sql нет встроенной функции для этого. Чтобы достичь того, что вы хотите сделать, вам действительно придется создать пользовательскую функцию и таблицу для хранения данных. Вам нужно, чтобы эту функцию можно было использовать во многих контекстах (с разными типами параметров) или параметры всегда будут в одном и том же порядке и типах? - person Alex Samson; 15.03.2018