Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/74.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么这个SQL函数在insert语句中的工作方式不同?_Sql_Sql Server_Tsql - Fatal编程技术网

为什么这个SQL函数在insert语句中的工作方式不同?

为什么这个SQL函数在insert语句中的工作方式不同?,sql,sql-server,tsql,Sql,Sql Server,Tsql,我在“voorwerpnummer”列中得到一个主键冲突。重复值为1,但函数不应给出相同的数字两次。当我尝试在“Voorwerp”表中没有任何内容的情况下测试函数时,它返回1,如果我在“Voorwerp”表中用一行测试它,它返回2;应该这样。为什么在这个insert查询中两次返回1?我正在使用SQL Server 2016 -- Empty tables DELETE FROM Bestand DELETE FROM Voorwerp_in_rubriek DELETE FROM Voorw

我在“voorwerpnummer”列中得到一个主键冲突。重复值为1,但函数不应给出相同的数字两次。当我尝试在“Voorwerp”表中没有任何内容的情况下测试函数时,它返回1,如果我在“Voorwerp”表中用一行测试它,它返回2;应该这样。为什么在这个insert查询中两次返回1?我正在使用SQL Server 2016

-- Empty tables
DELETE
FROM Bestand

DELETE
FROM Voorwerp_in_rubriek

DELETE
FROM Voorwerp

-- Delete function if exists
IF EXISTS (SELECT *
           FROM   sys.objects
           WHERE  object_id = OBJECT_ID(N'[dbo].[GetVoorwerpnummer]')
                  AND type IN ( N'FN', N'IF', N'TF', N'FS', N'FT' ))
  DROP FUNCTION [dbo].[GetVoorwerpnummer]
GO 

-- Returns the next item number
CREATE FUNCTION dbo.GetVoorwerpnummer()
RETURNS INTEGER
AS BEGIN
    DECLARE @Highest INTEGER
    -- Find the highest number
    SET @Highest = (SELECT TOP 1 voorwerpnummer FROM EenmaalAndermaal.dbo.Voorwerp ORDER BY voorwerpnummer DESC)
    -- Take 0 if there is none
    SET @Highest = ISNULL(@Highest, 0)
    -- Add 1
    SET @Highest = (@Highest + 1);

    RETURN @Highest
END
GO

INSERT INTO EenmaalAndermaal.dbo.Voorwerp (looptijd, looptijdbegin, startprijs, verzendkosten, verkoopprijs, beschrijving, betalingsinstructie, betalingswijzenaam, landnaam, plaatsnaam, titel, verzendinstructies, voorwerpnummer, wel_niet_indicator, verkoper, koper)
    SELECT  
            10 AS looptijd,
            GETDATE() AS looptijdbegin,
            LEFT(Prijs,8) AS startprijs,
            '1,20' AS verzendkosten,
            NULL AS verkoopprijs,
            'Beschrijving van product' AS beschrijving,
            NULL AS betalingsinstructie,
            'paypal' AS betalingswijzenaam,
            LEFT(land,40) AS landnaam,
            LEFT(Locatie,40) AS plaatsnaam,
            LEFT(Titel,40) AS titel,
            NULL AS verzendinstructies,
            dbo.GetVoorwerpnummer() AS voorwerpnummer,
            0 AS wel_niet_indicator,
            LEFT(Verkoper, 40) AS Verkoper,
            NULL AS koper
    FROM EBAY.dbo.Items

这是因为SQL是基于集合的,而不是基于行的

首先执行
SELECT
语句。这将产生一个数据集,所有数据集都具有相同的
voorwerpnummer
(因为尚未向
eenmalandermal.dbo.Voorwerp
添加任何内容)。您可以通过执行
SELECT
语句来测试这一点。整个集合都被插入到目标表中,因此后续调用
GetVoorwerpnummer()
将导致更高的
voorwerpnummer
(显然,如果
INSERT
会成功,如果
voorwerpnummer
是主键,则不会成功)

要更正它,我将执行以下操作:

-- Execute function once to fill variable, so it isn't needlessly executed for every row
DECLARE @Voorwerp INT = dbo.GetVoorwerpnummer();

INSERT INTO EenmaalAndermaal.dbo.Voorwerp (looptijd, looptijdbegin, startprijs, verzendkosten, verkoopprijs, beschrijving, betalingsinstructie, betalingswijzenaam, landnaam, plaatsnaam, titel, verzendinstructies, voorwerpnummer, wel_niet_indicator, verkoper, koper)
    SELECT  
            10 AS looptijd,
            GETDATE() AS looptijdbegin,
            LEFT(Prijs,8) AS startprijs,
            '1,20' AS verzendkosten,
            NULL AS verkoopprijs,
            'Beschrijving van product' AS beschrijving,
            NULL AS betalingsinstructie,
            'paypal' AS betalingswijzenaam,
            LEFT(land,40) AS landnaam,
            LEFT(Locatie,40) AS plaatsnaam,
            LEFT(Titel,40) AS titel,
            NULL AS verzendinstructies,
            COALESCE(@Voorwerp, 0) + ROW_NUMBER() OVER(ORDER BY id /*Choose a logical, preferably unique column here*/) AS voorwerpnummer,
            0 AS wel_niet_indicator,
            LEFT(Verkoper, 40) AS Verkoper,
            NULL AS koper
    FROM EBAY.dbo.Items
正如Stillgar在下面评论的那样,最好将PK定义为
IDENTITY
属性


此外,就个人而言(我不确定这是否是“官方”最佳实践),我更喜欢将主键列保留为表的第一列。

最有可能的问题是不提交插入;在提交之前,数据并不“真正”存在

以下是我的作品:

    CREATE TABLE dbo.PK_Test
    (
     PK         INT
    ,TXT        VARCHAR(0032)
    );

BEGIN TRANSACTION
    INSERT INTO dbo.PK_Test VALUES(05, 'First');
COMMIT TRANSACTION;


CREATE FUNCTION dbo.F_PK_Test()
RETURNS INTEGER
AS BEGIN
    DECLARE @Highest INTEGER
    -- Find the highest number
    SET @Highest = (SELECT TOP 1 PK FROM PK_Test ORDER BY PK DESC)
    -- Take 0 if there is none
    SET @Highest = ISNULL(@Highest, 0)
    -- Add 1
    SET @Highest = (@Highest + 1);

    RETURN @Highest
END;

SELECT * FROM dbo.PK_Test;

BEGIN TRANSACTION
    INSERT INTO Adhoc_Area.dbo.PK_Test (PK, Txt) VALUES (Adhoc_Area.dbo.F_PK_Test(), Adhoc_Area.dbo.F_PK_Test());
    INSERT INTO Adhoc_Area.dbo.PK_Test (PK, Txt) VALUES (Adhoc_Area.dbo.F_PK_Test(), Adhoc_Area.dbo.F_PK_Test());
COMMIT TRANSACTION;


SELECT * FROM dbo.PK_Test;

如果一次插入一组(多条记录),而不是一条记录,会发生什么?例如:
INSERT INTO tmp.PK_Test从INFORMATION_SCHEMA.TABLES中选择dbo.F_PK_Test(),''作为T
,然后它就会断开,我错过了使它成为set操作的FROM部分。完全是我的错。所以现在的问题是,为什么不使用IDENTITY而不是手动管理唯一的序列号呢?我考虑过这一点,但如果可能的话,我宁愿不对表进行任何更改。也许是序列号?