加快表值函数

时间:2017-10-06 02:33:58

标签: sql-server tsql

我创建了一个创建随机地址的函数,但每次调用花费的时间太长(大约10 - 20秒)。我必须在超过900,000条记录上运行此功能,并且通过我对此功能的计时的计算,这需要120天给予或接受。这是功能:

CREATE function dbo.fn_GetAddress2 (@state NVARCHAR(20))
returns @NewAddress TABLE
(   
    Address1    NVARCHAR(MAX),
    Address2    NVARCHAR(MAX),
    City        NVARCHAR(MAX),
    Postcode    NVARCHAR(MAX)
)
AS
BEGIN
    DECLARE @Address1   NVARCHAR(MAX)
    DECLARE @Address2   NVARCHAR(MAX)
    DECLARE @City       NVARCHAR(MAX)
    DECLARE @Postcode   NVARCHAR(MAX)
    DECLARE @StreetPID  NVARCHAR(MAX)
    DECLARE @newID1     NVARCHAR(36)

    SELECT @StreetPID =
         ( SELECT TOP 1 g.street_locality_pid AS StreetPID 
            FROM [GNAF].dbo.Street_Locality g
                INNER JOIN [GNAF].dbo.Address_Detail aD ON g.street_locality_pid = aD.street_locality_pid
            WHERE g.street_name IS NOT NULL AND g.state != @state AND aD.flat_number IS NOT NULL
            ORDER BY  (SELECT new_id FROM getNewID ))

    SELECT @Address1 =
         ( SELECT TOP 1 CAST(aD.flat_number AS VARCHAR(20)) + ' ' + g.Street_name + ' ' + g.street_type_code AS Address1 
            FROM [GNAF].dbo.Street_Locality g
                INNER JOIN [GNAF].dbo.Address_Detail aD ON g.street_locality_pid = aD.street_locality_pid
            WHERE g.street_name IS NOT NULL AND g.state != @state AND aD.flat_number IS NOT NULL
                    AND g.street_locality_pid = @StreetPID
            ORDER BY  (SELECT new_id FROM getNewID ))


    SELECT @postcode =
         ( SELECT TOP 1 aD.postcode AS postcode 
            FROM [GNAF].dbo.Street_Locality g
                INNER JOIN [GNAF].dbo.Address_Detail aD ON g.street_locality_pid = aD.street_locality_pid
            WHERE g.street_name IS NOT NULL AND g.state != @state AND aD.flat_number IS NOT NULL
                    AND g.street_locality_pid = @StreetPID
            ORDER BY  (SELECT new_id FROM getNewID ))

    SELECT @City =
         ( SELECT TOP 1 l.locality_name AS city 
            FROM [GNAF].dbo.Street_Locality g
                INNER JOIN [GNAF].dbo.Address_Detail aD ON g.street_locality_pid = aD.street_locality_pid
                INNER JOIN [GNAF].dbo.Locality l ON aD.locality_pid = l.locality_pid
            WHERE g.street_name IS NOT NULL AND g.state != @state AND aD.flat_number IS NOT NULL
                    AND g.street_locality_pid = @StreetPID
            ORDER BY  (SELECT new_id FROM getNewID ))

    IF @Address1 IS NOT NULL 
    BEGIN
        INSERT @NewAddress
        SELECT @Address1, @Address2, @city, @postcode;
    END;
    Return;
END
GO

[GNAF]数据库是一个庞大的数据库,充满了澳大利亚的每一个地址。函数和newid()对我来说是全新的。

我尝试过几种不同的方法,包括CTE:

SET @State = 'NSW'
;WITH CTE AS (
    SELECT TOP 1 CAST(aD.flat_number AS VARCHAR(20)) + ' ' + g.Street_name + ' ' + g.street_type_code AS Address1 
            , aD.postcode AS postcode
    FROM [GNAF].dbo.Street_Locality g
        INNER JOIN [GNAF].dbo.Address_Detail aD ON g.street_locality_pid = aD.street_locality_pid
    WHERE g.street_name IS NOT NULL AND g.state != @state AND aD.flat_number IS NOT NULL
    ORDER BY  (SELECT new_id FROM getNewID )
)
SELECT  @Address1 = ( SELECT Address1 FROM CTE )
        ,@postcode = ( SELECT postcode FROM CTE )
SELECT @Address1
        , @postcode

这实际上比较慢。对此有任何帮助将非常感激。

2 个答案:

答案 0 :(得分:1)

这里有适合你的东西。请注意:我只是创建了5个新表,每个地址部分对应一个表,并使用地址表中的数据填充它们,而不是来回到完整的地址表。除了状态表之外,我使用了2000。您可以使用更多或更少,只需确保更改函数中的模数值以匹配您在每个表中的行数。

在任何情况下,它都很快......我将发布SET STATISTICS IO,TIME数字基于10,000,100,000&正在生成1,000,000行。

USE tempdb;
GO 
-- Populate a series of individual tables one for each part of the address...
CREATE TABLE dbo.a1 (ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED, Address1 VARCHAR(60) );
INSERT dbo.a1 (Address1)
SELECT TOP 2000 b.PhysAddr1 FROM Xyz.dbo.ContactBranch b WHERE b.PhysAddr1 LIKE '[0-Z ][0-Z ][0-Z ][0-Z ][0-Z ]%';

CREATE TABLE dbo.a2 (ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED, Address2 VARCHAR(50) );
INSERT dbo.a2 (Address2)
SELECT TOP 2000 ISNULL(b.PhysAddr2, '') FROM Xyz.dbo.ContactBranch b;

CREATE TABLE dbo.cty (ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED, City VARCHAR(50) );
INSERT dbo.cty (City)
SELECT TOP 2000 b.PhysCity FROM Xyz.dbo.ContactBranch b WHERE b.PhysCity LIKE '[0-Z ][0-Z ][0-Z ][0-Z ][0-Z ]%';

CREATE TABLE dbo.st (ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED, State CHAR(2));
INSERT dbo.st (State)
SELECT s.Description FROM Xyz.dbo.LK_States s WHERE s.Description LIKE '[a-Z][a-Z]';

CREATE TABLE dbo.zip (ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED, Zip VARCHAR(5) );
INSERT dbo.zip (Zip)
SELECT TOP 2000 LEFT(b.PhysZip10, 5) FROM Xyz.dbo.ContactBranch b WHERE b.PhysZip10 LIKE '[0-Z ][0-Z ][0-Z ][0-Z ][0-Z ]%';

/*  DROP TABLE dbo.a1; DROP TABLE dbo.a2; DROP TABLE dbo.cty; DROP TABLE dbo.st; DROP TABLE dbo.zip; */
/*
(2000 rows affected)
(2000 rows affected)
(2000 rows affected)
(52 rows affected)
(2000 rows affected)
*/

功能代码......

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO 
CREATE FUNCTION dbo.tfn_AddressGenerator
/* ===================================================================
10/06/2017 JL, Created: to randomly generate random addresses.
    The general premmise is based on the Ben-Gan" or inline Tally table.
=================================================================== */
--===== Define I/O parameters
(
    @State CHAR(2),
    @NumToCreate INT 
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN

    WITH 
        cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),   --rows
        cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),                             -- 100 rows
        cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b),                             -- 10,000 rows
        cte_Tally (n) AS (
            SELECT TOP (@NumToCreate)
                ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
            FROM
                cte_n3 a CROSS JOIN cte_n3 b                                                    -- 100,000,000 rows
            )
    SELECT 
        a1.Address1,
        a2.Address2,
        c.City,
        State = IIF(s1.State = @State, s2.State, s1.State),
        z.Zip
    FROM
        cte_Tally t
        CROSS APPLY ( VALUES (
            ABS(CHECKSUM(t.n)) % 2000 + 1, ABS(CHECKSUM(t.n)) % 1528 + 1,
            ABS(CHECKSUM(t.n)) % 2000 + 1, ABS(CHECKSUM(t.n)) % 52 + 1,
            ABS(CHECKSUM(t.n)) % 52 + 1,   ABS(CHECKSUM(t.n)) % 2000 + 1 
            ) ) x (Add1, Add2, City, State1, State2, Zip)
        CROSS APPLY (SELECT TOP 1 dbo.a1.Address1 FROM dbo.a1    WHERE x.Add1 = dbo.a1.ID) a1
        CROSS APPLY (SELECT TOP 1 dbo.a2.Address2 FROM dbo.a2    WHERE x.Add2 = dbo.a2.ID) a2
        CROSS APPLY (SELECT TOP 1 dbo.cty.City    FROM dbo.cty   WHERE x.City = dbo.cty.ID) c
        CROSS APPLY (SELECT TOP 1 dbo.st.State  FROM dbo.st    WHERE x.State1 = dbo.st.ID) s1
        CROSS APPLY (SELECT TOP 1 dbo.st.State  FROM dbo.st    WHERE x.State2 = dbo.st.ID) s2
        CROSS APPLY (SELECT TOP 1 dbo.Zip.Zip     FROM dbo.zip   WHERE x.Zip = dbo.zip.ID) z;
GO

该功能的实际执行......

SELECT ag.Address1, ag.Address2, ag.City,ag.State, ag.Zip
FROM dbo.tfn_AddressGenerator('FL',10000) ag;

示例输出......

Address1                    Address2    City             State Zip
--------------------------- ----------- ---------------- ----- -----
111 CONGRESSIONAL BLVD                  ATLANTA          AL    30042
414 Eagle Rock Ave # 100    STE 400     MARIETTA         AR    70816
414 Eagle Rock Ave Ste 107  Suite 300   NORCROSS         AZ    72116
3931 HIGHWAY 78 W STE B200              SAVANNAH         CA    31702
4728 Joseph Eli Dr          STE 6       STONE MOUNTAIN   CO    30338
29620 IH10 West                         DULUTH           CT    63026
4666 El Camino Real                     ATLANTA          DC    60555
3700 Thomas Rd Ste 215      STE 100     ATLANTA          DE    32241
3700 Thomas Rd Ste 215      STE B-2190  ALPHARETTA       FL    36117
2615 East West Connector                ALPHARETTA       GA    35201

10,000行结果......

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(10000 rows affected)
Table 'zip'. Scan count 0, logical reads 20000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'st'. Scan count 0, logical reads 40000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'cty'. Scan count 0, logical reads 20000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'a2'. Scan count 0, logical reads 20000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'a1'. Scan count 0, logical reads 20000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 93 ms.

100,000行结果......

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(100000 rows affected)
Table 'zip'. Scan count 0, logical reads 200000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'st'. Scan count 0, logical reads 400000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'cty'. Scan count 0, logical reads 200000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'a2'. Scan count 0, logical reads 200000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'a1'. Scan count 0, logical reads 200000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 907 ms,  elapsed time = 948 ms.

1,000,000行结果......

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.
SQL Server parse and compile time: 
   CPU time = 31 ms, elapsed time = 51 ms.

(1000000 rows affected)
Table 'a1'. Scan count 0, logical reads 4000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'a2'. Scan count 0, logical reads 3056, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'cty'. Scan count 0, logical reads 4000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'st'. Scan count 0, logical reads 208, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'zip'. Scan count 0, logical reads 4000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 10921 ms,  elapsed time = 15743 ms.

不到一秒钟内100K行&在~15秒内有100万行...

答案 1 :(得分:0)

想想最简单的方法是使用金额变量对每个州运行它,这里是代码:

 DECLARE @states TABLE (name NVARCHAR(50));

INSERT INTO @states (name)
SELECT DISTINCT
    State
FROM anon_AddressChange


DECLARE @count INT
DECLARE @i  INT
SET @i = 0
SET @count = (SELECT COUNT(*) FROM @states)

while @i < @count 
BEGIN

    DECLARE @state NVARCHAR(MAX)
    SET @State = ( SELECT top 1 name from @states order by name )

    DECLARE @amount INT
    SET @amount = ( SELECT count(*) FROM anon_addresschange where state = @state )





    ;WITH CTE AS (
        SELECT TOP (@amount) CAST(aD.flat_number AS VARCHAR(20)) + ' ' + g.Street_name + ' ' + g.street_type_code AS Address1 
                , aD.postcode AS postcode
                , l.locality_name AS city 

        FROM [GNAF].dbo.Street_Locality g
            INNER JOIN [GNAF].dbo.Address_Detail aD ON g.street_locality_pid = aD.street_locality_pid
            INNER JOIN [GNAF].dbo.Locality l ON aD.locality_pid = l.locality_pid
        WHERE g.street_name IS NOT NULL AND g.state = @state AND aD.flat_number IS NOT NULL
            AND g.state NOT IN ('OT', 'NT' ,'TAS' ,'VIC' ,'ACT')
        ORDER BY  (SELECT new_id FROM getNewID )
    )
    UPDATE anon_addresschange SET
        newStreet1      = UPPER(LEFT(a.Address1,1))+LOWER(SUBSTRING(a.Address1,2,LEN(a.Address1)))
        ,newCity        = UPPER(LEFT(a.city,1))+LOWER(SUBSTRING(a.city,2,LEN(a.city)))
        ,newPostcode    = a.postcode
        ,newState       = @state
        ,newCountry     = 'Australia'
    FROM (
    SELECT  *,  ROW_NUMBER() OVER (ORDER BY CAST(GETDATE() AS TIMESTAMP)) AS RowNumber from cte ) a
    CROSS APPLY (
    SELECT *, ROW_NUMBER() OVER (ORDER BY CAST(GETDATE() AS TIMESTAMP)) AS RowNumber FROM anon_AddressChange
     WHERE state = @state) b
     WHERE a.Rownumber = b.Rownumber
        AND anon_addresschange.personID = b.personID


     SET @i = @i + 1
     delete from @states WHERE NAME IN ( SELECT TOP 1 name FROM @states order by name )
END

我真正需要做的就是在更新/插入语句中使用它。

这需要2秒才能运行1003条记录,因此1,000,000条记录需要33分钟。