简体   繁体   English

SQL设置操作根据另一个表上的值更新表

[英]SQL set operation to update table based on values on another table

i am working with two tables - one for Addresses and another for MasterAddressCodes. 我正在使用两个表-一个用于地址,另一个用于MasterAddressCodes。

I need to update the Address table with the locationcode for the address as it is in the MasterAddressCodes table. 我需要使用MasterAddressCodes表中的地址的位置代码更新Address表。

This is already scripted using a cursor, but it is hugely inefficient and takes over 2 hours to complete (225000 records approx), and I know that there has to be a more elegant and performant solution. 这已经使用游标编写了脚本,但是它效率极低,并且需要2个小时才能完成(大约225000条记录),而且我知道必须有一个更优雅,更高效的解决方案。

Here are the table details: 以下是表格的详细信息:

Address: 地址:

CREATE TABLE [dbo].[Address](
    [addressID] [int] IDENTITY(1,1) NOT NULL,
    [number] [varchar](12) NULL,
    [street] [varchar](30) NULL,
    [tag] [varchar](20) NULL,
    [prefix] [varchar](10) NULL,
    [apt] [varchar](17) NULL,
    [city] [varchar](24) NULL,
    [state] [varchar](2) NULL,
    [zip] [varchar](10) NULL,
    [location_code] [varchar](40) NULL,
    [postOfficeBox] [bit] NOT NULL,
)

MasterAddressCode: MasterAddressCode:

CREATE TABLE [dbo].[MasterAddressCode](
    [Street Name] [varchar](50) NULL,
    [From] [varchar](50) NULL,
    [To] [varchar](50) NULL,
    [Code] [varchar](50) NULL,
    [LocationCode] [varchar](50) NULL,
    [Tag] [varchar](10) NULL,
    [Prefix] [varchar](10) NULL
)

Here's what I have so far: 这是我到目前为止的内容:

select locationcode from MasterAddressCode tmp
join address a on a.street = tmp.[street name], a.prefix = tmp.prefix, a.tag = tmp.tag
where a.number between tmp.[from] and tmp.[to]

And here is where I am stuck. 这就是我被困住的地方。 The address [number] column has values like you would expect - 123, 4567, 8899988 -- but there are also exceptions like 123 1/2, 345 1/3, 678 1/4. address [number]列具有您期望的值-123、4567、8899988-但也有例外,例如123 1 / 2、345 1 / 3、678 1/4。

In addition, the match to the MasterAddressCode will also depend on if the number is even or odd - but not for all addresses, only those that have an 'E' for Even or 'O' for Odd in the MasterAddressCode.[code] column. 另外,与MasterAddressCode的匹配还取决于数字是偶数还是奇数-但不是所有地址,仅取决于MasterAddressCode。[code]列中具有偶数“ E”或奇数“ O”的地址。 。

The cursor script is doing the following: 游标脚本正在执行以下操作:

-- Iterate through all records in address
-- if location code does not match what is in MasterAddressCode, update the location code
DECLARE @addID INT
DECLARE @street varchar (30)
DECLARE @tag VARCHAR(20)
DECLARE @prefix VARCHAR(10)
DECLARE @number VARCHAR(12)
DECLARE @num INT
DECLARE @recCt INT
DECLARE @newLocCode VARCHAR(40)

DECLARE add_cursor CURSOR FOR
     select addressid from address



OPEN add_cursor
FETCH NEXT from add_cursor into @addID



WHILE @@FETCH_STATUS = 0
     BEGIN



     Print @addID;

          set @street = (select street from address where addressid = @addID)
          set @tag = (select tag from address where addressid = @addID)
          set @number = (select number from address where addressid = @addID)
          set @prefix = (select prefix from address where addressid = @addID)

          --cast number as int and remove 1/2 if there
          IF @number like ('%1/2') or @number like ('%1/3')or @number like ('%1/4')
                    BEGIN
                         set @number = LEFT(@number, LEN(@number) - 3)
                    END

           set @recCt = (select count(*) from MasterAddressCode where [Street Name] = @street
                                                                                and tag = @tag 
                                                                                and prefix = @prefix   
                                                                                and (@num between [From] and [To] ))

          --check for the street to be 'odd'
          IF @recCt > 1 and (@num%2)<>0 and (@street is not NULL or @street != '') and (@number is not NULL)
                    BEGIN
                    set @newLocCode = (SELECT LocationCode FROM MasterAddressCode
                                                  WHERE (@num between [From] and [To])
                                                       AND [Street Name] = @street
                                                       AND tag = @tag
                                                       AND Code = 'O' )
                    PRINT 'Odd ' + @number + ' ' + @newLocCode

                    END
         --check for the street to be 'even'
          IF @recCt > 1 and (@num%2)=0 and (@street is not NULL or @street != '') and (@number is not NULL)
                    BEGIN
                    set @newLocCode = (SELECT LocationCode FROM MasterAddressCode
                                                  WHERE (@num between [From] and [To])
                                                       AND [Street Name] = @street
                                                       AND tag = @tag
                                                       AND Code = 'E' )

                    PRINT 'Even ' + @number + ' ' + @newLocCode

                    END

         --default update
          IF @recCt = 1 and (@street is not NULL or @street != '') and (@number is not NULL)
                    BEGIN
                    set @newLocCode = (SELECT LocationCode FROM MasterAddressCode
                                                  WHERE (@num between [From] and [To])
                                                       AND [Street Name] = @street
                                                       AND tag = @tag
                                                       AND Code = '' or Code is NULL)


                    PRINT 'Default ' + @number + ' ' + @newLocCode
                    END --else

          IF @street is NULL or @number is NULL
          BEGIN
          set @newLocCode = NULL
          PRINT 'NULL in number or street'
          END

        update address set location_code = @newLocCode where addressid = @addID

          FETCH NEXT FROM add_cursor INTO @addID


     END --while

     CLOSE add_cursor
     DEALLOCATE add_cursor

So...any suggestions would be appreciated! 所以...任何建议将不胜感激!

Well I put this together and I hope (fingers crossed) it should work 好吧,我把它们放在一起,希望(手指交叉)它应该工作

;WITH A AS 
(
SELECT *,
      CASE
         WHEN  number like '%1/2' or number like '%1/3' or number like '%1/4'
           THEN LEFT(number, LEN(number) - 3)
        ELSE number
       END   AS New_Number
FROM [address]
)
update A 
   set A.location_code = CASE 
                          WHEN MA2.recCt > 1 and (A.New_Number%2)<>0 and (A.street is not NULL or A.street != '')
                               and (A.New_Number is not NULL) AND MA.Code = 'O'
                            THEN MA.LocationCode

                          WHEN MA2.recCt > 1 and (A.New_Number%2)=0 and (A.street is not NULL or A.street != '')
                               and (A.New_Number is not NULL) AND MA.Code = 'E'
                            THEN MA.LocationCode

                          WHEN MA2.recCt = 1 and (A.street is not NULL or A.street != '') and (A.New_Number is not NULL)
                               AND (MA.Code = '' or MA.Code is NULL)
                            THEN MA.LocationCode

                          WHEN A.New_Number IS NULL OR A.street IS NULL
                             THEN NULL
                        END
FROM  A 
INNER JOIN MasterAddressCode MA ON A.tag    = MA.tag
                               AND A.prefix = MA.prefix
                               AND A.street = MA.[Street Name]
                               AND A.New_Number 
                               BETWEEN MA.[from] AND MA.[to]
INNER JOIN 
( SELECT [Street Name] , tag, prefix, COUNT(*) AS recCt
  FROM MasterAddressCode MA 
  INNER JOIN [address] A ON A.tag    = MA.tag
                        AND A.prefix = MA.prefix
                        AND A.street = MA.[Street Name]
                        AND A.New_Number 
                        BETWEEN MA.[from] AND MA.[to]
  GROUP BY [Street Name] , tag, prefix
 ) MA2 ON A.tag    = MA2.tag
        AND A.prefix = MA2.prefix
        AND A.street = MA2.[Street Name]
        AND A.New_Number 
        BETWEEN MA2.[from] AND MA2.[to]

Without data to test against, I have no idea whether I got this right or what the performance will be like, but I am fairly certain I included every piece of logic from your iterator. 没有可以测试的数据,我不知道自己是否正确,或者性能如何,但是我可以肯定地说,我包括了迭代器中的所有逻辑。 You can check it out here or see below: 您可以在此处查看或查看以下内容:

;WITH allMasterAddresses AS (
  SELECT [Street Name] AS StreetName, Tag, Prefix, [From], [To], [Code], [LocationCode]
  FROM dbo.MasterAddressCode
)
, addressWithStrippedNumber AS (
  SELECT addressID
    , CASE 
        WHEN RIGHT(address.number, 3) IN ('1/2', '1/3', '1/4') THEN LEFT(address.number, LEN(address.number) - 3) 
        ELSE address.number 
      END AS number
  FROM dbo.Address address
)
/* Run this SELECT instead of the UPDATE to evaluate results before updating */
/*
SELECT address.addressID 
  , address.street
  , address.tag
  , address.prefix
  , addressStripped.number
  , address.location_code AS CurrentLocationCode
  , CASE 
      WHEN address.street IS NULL OR addressStripped.number IS NULL THEN NULL
      ELSE masterAddress.LocationCode
    END AS NewLocationCode
*/
UPDATE address
SET address.location_code = CASE 
    WHEN address.street IS NULL OR addressStripped.number IS NULL THEN NULL
    ELSE masterAddress.LocationCode
  END
FROM dbo.Address address
INNER JOIN addressWithStrippedNumber addressStripped ON address.addressID = addressStripped.addressID
INNER JOIN (
  SELECT StreetName, Tag, Prefix, [From], [To], COUNT(0) AS Total
  FROM allMasterAddresses
  GROUP BY StreetName, Tag, Prefix, [From], [To]
) AS masterAddresses ON address.street = masterAddresses.StreetName
  AND address.tag = masterAddresses.Tag
  AND address.prefix = masterAddresses.Prefix
  AND addressStripped.number BETWEEN masterAddresses.[From] and masterAddresses.[To]
  AND masterAddresses.Total > 0
INNER JOIN allMasterAddresses AS masterAddress ON address.street = masterAddress.StreetName
  AND address.tag = masterAddress.Tag
  --AND address.prefix = masterAddress.Prefix /* this isn't in your original logic, but don't you need it? */
  AND addressStripped.number BETWEEN masterAddress.[From] and masterAddress.[To]
  AND isNull(masterAddress.Code, '') = CASE 
    WHEN masterAddresses.Total = 1 THEN ''
    ELSE CASE 
      WHEN addressStripped.number % 2 != 0 THEN 'O'
      ELSE 'E'
    END
  END
WHERE isNull(address.street, '') != ''
  AND addressStripped.number IS NOT NULL;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM