2

基本的に既存のテーブル (約 150,000 行) を ID で更新する SQL ストアド プロシージャを作成する必要があります。

このストアド プロシージャが実行するテーブルは、基本的に、人、名前、住所などのリストです。

個人の ID のアルゴリズムは次のとおりです。 - 個人の名の最初の 4 文字までを使用します。- 人の姓の最初の 2 文字までとります。- フィールドが 8 文字になるまで、残りを 0 で埋め、最後にカウント番号を付けます。

たとえば、JOHN SMITH という名前の ID は「JOHNSM00」になります。JOHN SMITH が 2 人いた場合、次の人物の ID は JOHNSM01 になります。たとえば、個人名が FI LYNN の場合、ID は FILY0000 になります。

私が書いた次のストアド プロシージャがありますが、実行に約 9 時間かかります。私が見逃しているこれを行うためのより良い方法はありますか?

ALTER PROCEDURE [dbo].[LM_SP_UPDATE_PERSON_CODES]
AS

DECLARE @NAMEKEY NVARCHAR(10)
DECLARE @NEWNAMEKEY NVARCHAR(10)
DECLARE @LENGTH INT
DECLARE @KEYCOUNT INT
DECLARE @I INT
DECLARE @PADDING NVARCHAR(8)
DECLARE @PERSONS CURSOR
DECLARE @FIRSTNAME NVARCHAR(30)
DECLARE @LASTNAME NVARCHAR(30)

SET @PADDING = '00000000'
--FIRST CLEAR OLD NEW NAMEKEYS IF ANY EXIST
UPDATE LM_T_PERSONS SET NEW_NAMEKEY = NULL
SET @PERSONS = CURSOR FOR
SELECT NAMEKEY, NAME_2, NAME_1 FROM LM_T_PERSONS

OPEN @PERSONS
FETCH NEXT FROM @PERSONS INTO @NAMEKEY, @FIRSTNAME, @LASTNAME

WHILE @@FETCH_STATUS = 0
BEGIN
    --CHECK THE LENGTH OF FIRST NAME TO MAKE SURE NOTHING EXCEEDS 4
    SET @LENGTH = LEN(@FIRSTNAME)
    IF @LENGTH > 4 
        SET @LENGTH = 4

    SET @NEWNAMEKEY = SUBSTRING(@FIRSTNAME,1,@LENGTH)

    --CHECK THE LENGTH OF LAST NAME TO MAKE SURE NOTHING EXCEEDS 2
    SET @LENGTH = LEN(@LASTNAME)
    IF @LENGTH > 2
        SET @LENGTH = 2

    SET @NEWNAMEKEY = @NEWNAMEKEY + SUBSTRING(@LASTNAME,1,@LENGTH)
    SET @LENGTH = LEN(@NEWNAMEKEY)
    SET @I = 0
    SET @PADDING = SUBSTRING('00000000',1,8 - LEN(@NEWNAMEKEY) - LEN(CONVERT(NVARCHAR(8),@I)))
    --SEE IF THIS KEY ALREADY EXISTS
    SET @KEYCOUNT = (SELECT COUNT(1) FROM LM_T_PERSONS WHERE NEW_NAMEKEY = @NEWNAMEKEY + @PADDING + CONVERT(NVARCHAR(8),@I) )
    WHILE @KEYCOUNT > 0
    BEGIN
        SET @I = @I+1
        SET @PADDING = SUBSTRING('00000000',1,8 - LEN(@NEWNAMEKEY) - LEN(CONVERT(NVARCHAR(8),@I)))
        SET @KEYCOUNT = (SELECT COUNT(1) FROM LM_T_PERSONS WHERE NEW_NAMEKEY = @NEWNAMEKEY + @PADDING + CONVERT(NVARCHAR(8),@I) )
    END
    UPDATE LM_T_PERSONS SET NEW_NAMEKEY = @NEWNAMEKEY + @PADDING + CONVERT(NVARCHAR(8),@I) WHERE NAMEKEY = @NAMEKEY

    FETCH NEXT FROM @PERSONS INTO @NAMEKEY, @FIRSTNAME, @LASTNAME
END
CLOSE @PERSONS
DEALLOCATE @PERSONS
4

4 に答える 4

6

このようなものは、カーソルなしでそれを行うことができます:

UPDATE P
SET NAMEKEY = FIRSTNAME + LASTNAME + REPLICATE('0', 8 - LEN(FIRSTNAME) - LEN(LASTNAME) - LEN(I)) + I
FROM
  LM_T_PERSONS AS P JOIN
  (
    SELECT
      NAMEKEY,
      LEFT(NAME_2, 4) AS FIRSTNAME,
      LEFT(NAME_1, 2) AS LASTNAME,
      CONVERT(NVARCHAR, ROW_NUMBER() OVER(PARTITION BY LEFT(NAME_2, 4), LEFT(NAME_1, 2) ORDER BY NAMEKEY)) AS I
    FROM
      LM_T_PERSONS
  ) AS DATA
  ON P.NAMEKEY = DATA.NAMEKEY

ここでクエリを確認できます: http://sqlfiddle.com/#!3/47365/19

于 2013-03-21T04:43:58.783 に答える
3

私は厳密な「XYZ の方法で行う必要があります」というわけではありませんが、過去の同様の種類の演習から:

  1. ストアド プロシージャを維持したい場合、週末のように (時間的に) 長いタスクを実行できるウィンドウがあり、実行中の唯一の操作であり、データベースを設定することができます。作業中に単純復旧モード (Prod データベースで作業していると仮定して、完全復旧モードになっていると仮定します) にすると、処理速度が向上する可能性があります (トランザクション ログに書き込みを行っていないため、そうではありません。つまり、何かをしているのは自分だけであることを確認したい限られた回復可能性です)。万が一の事態に備えて、仕事を始める前に完全なバックアップを取ります

  2. 主にセットベースの手続き型コードをどこかで実行しているため、ストアドプロシージャではなく、カーソルの使用、部分文字列などだと思います。これらが存在する理由の背後にある「理由」は理解していますが、オプションはそれを取り出してSQL Server Integration Servicesのようなものを使用することです。つまり、個々の行に対してループまたは変換を行うのにより適したテクノロジーオプションを使用することです

  3. 手続き型の作業により適したものを使用することに続いて...単純な.NETアプリケーションなどをいつでも作成できます。私自身の(限られた)経験から言えば、これが過去に行われたのを見たことがありますが、マイレージは、操作の複雑さ(あなたの場合は UserId フィールドを変換するという点で十分に単純に聞こえます)、ボリューム、それを書いている人...私はそれが特にうまくいくのを見たことがないと言います(振り返って「それはすごかった」と言ったことはありませんでした)が、仕事が終わったように、何かに移ります.それ以外の場合は、経験から良いことも悪いこともありません(単に「平均」)。

DBからこれらのレコードを抽出し、必要な操作を実行できるため、SSISは良い方法だと思います(SSISは、.NETコードの記述を含む、データに対して実行できる非常に幅広い種類のことをサポートしていることを考慮してください{VB.NETとはいえfrom memory} 必要に応じて)、データベースを更新します。

他の種類の ETL テクノロジでもおそらく同様のことが可能になりますが、私は SSIS に最も精通しています。150k 行は、はるかに大きなボリュームを処理できるため、大きな問題にはなりません。私自身の経験から、あまり特別なことをしないSSISパッケージを作成しますが、約15分で100万行を超えるこの種の操作を実行できます...専門家はまだ少し遅いと言うでしょう:-)

ちょっとHTH、ネイサン

于 2013-03-21T04:07:42.577 に答える
1

このクエリは、必要なものを正確に取得し、はるかに高速に取得します。


select FirstName, 
       LastName, 
       ID + replicate('0',8-len(ID)-len(cast(rankNumber as varchar)))+cast(rankNumber as varchar)
from (
        select dense_rank() over (partition by id order by rownumber) rankNumber,
        FirstName, 
        LastName,
ID from ( select row_number() over (Order by FirstName) rownumber, FirstName, LastName, RTRIM(cast(FirstName as char(4)))+ RTRIM(cast(LastName as char(2))) as ID from person ) A ) B

于 2013-03-21T04:38:43.130 に答える
0

既存WHILEの最大シーケンス番号サフィックス ( ) を取得して内部ループの使用を回避し、0 を超える場合は 1 を追加し、NULL を返す場合は 0 を追加します。@INEW_NAMEKEY

于 2013-03-21T04:21:52.233 に答える