14

開発とテストで使用する本番データをスクランブルする独自の方法を探しています。ランダムな社会保障番号を作成したり、生年月日を変更したり、電子メールをごちゃまぜにしたりするスクリプトをいくつか作成しましたが、顧客名をごちゃまぜにする壁にぶつかりました。実名を維持したいので、引き続き使用または検索できるため、ランダムな文字生成は行われません。私がこれまでに試したことは、テーブル内のすべての姓の一時テーブルを作成し、一時テーブルからランダムに選択して顧客テーブルを更新することです。このような:

DECLARE @Names TABLE (Id int IDENTITY(1,1),[Name] varchar(100))

/* Scramble the last names (randomly pick another last name) */
INSERT @Names SELECT LastName FROM Customer ORDER BY NEWID();
WITH [Customer ORDERED BY ROWID] AS
(SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS ROWID, LastName FROM Customer)
UPDATE [Customer ORDERED BY ROWID] SET LastName=(SELECT [Name] FROM @Names WHERE ROWID=Id)

これはテストではうまく機能しましたが、大量のデータ (40,000 行で 20 分以上) の処理は完全に行き詰まりました。

実名と実稼働データの重みを維持しながら、顧客名をどのようにスクランブルしますか?

更新:失敗することはありません。投稿にすべての情報を入力しようとすると、重要なことを忘れてしまいます。このデータは、公開されている販売およびデモ環境でも使用されます。答えのいくつかは、名前を「切り替える」ために私がやろうとしていることですが、私の質問は文字通り、T-SQLでコーディングする方法ですか?

4

12 に答える 12

6

私はgenerateddataを使用します。これは、あらゆる種類のダミー データを生成できるオープン ソースの php スクリプトです。

于 2008-10-03T21:04:41.253 に答える
2

非常に簡単な解決策は、テキストを ROT13 することです。

より良い質問は、なぜデータをスクランブルする必要があると感じるのですか? 暗号化キーがある場合は、DES や AES などを介してテキストを実行することも検討できます。ただし、パフォーマンスの問題が発生する可能性があります。

于 2008-10-03T21:04:10.943 に答える
2

そのようなことをするとき、私は通常、最初に多くの名前と姓を 2 つの配列にロードし、次に配列からランダムな名前/姓を使用してデータベースを更新する小さなプログラムを作成します。非常に大きなデータセット (200.000 以上のレコード) でも非常に高速に動作します

于 2008-10-03T21:08:35.520 に答える
2

名前の文字を英語名の使用頻度と同じ「範囲」にある別の文字に変更する方法を使用します。どうやら、名前の文字の分布は、通常の会話英語とは異なります。たとえば、「x」と「z」は 0.245% の確率で発生するため、入れ替わります。もう一方の極端な例として、"w" は 5.5% の頻度で使用され、"s" は 6.86%、"t" は 15.978% 使用されます。「s」を「w」に、「t」を「s」に、「w」を「t」に変更します。母音は別の母音にのみ置き換えられるように、母音「aeio」を別のグループに保持します。同様に、"q"、"u"、"y" はまったく置き換えられません。私のグループ化と決定は完全に主観的なものです。

主に頻度に基づいて、2〜5文字の7つの異なる「グループ」になりました。各グループ内の文字は、同じグループ内の他の文字と交換されます。

最終的な結果は、名前のように見える名前ですが、「ここではない」からです。

Original name     Morphed name
Loren             Nimag
Juanita           Kuogewso
Tennyson          Saggywig
David             Mijsm
Julie             Kunewa

「TitleCase」関数を含む、私が使用する SQL を次に示します。ウェブ上で見つけた文字の頻度の違いに基づいて、「モーフィングされた」名前には 2 つの異なるバージョンがあります。

--    from     https://stackoverflow.com/a/28712621

-- Convert and return param as Title Case

CREATE FUNCTION [dbo].[fnConvert_TitleCase] (@InputString VARCHAR(4000) )
RETURNS VARCHAR(4000)AS
BEGIN
DECLARE @Index INT
DECLARE @Char CHAR(1)
DECLARE @OutputString VARCHAR(255)

SET @OutputString = LOWER(@InputString)
SET @Index = 2
SET @OutputString = STUFF(@OutputString, 1, 1,UPPER(SUBSTRING(@InputString,1,1)))

WHILE @Index <= LEN(@InputString)
BEGIN
   SET @Char = SUBSTRING(@InputString, @Index, 1)
   IF @Char IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&','''','(','{','[','@')
      IF @Index + 1 <= LEN(@InputString)
      BEGIN
         IF @Char != ''''  OR  UPPER(SUBSTRING(@InputString, @Index + 1, 1)) != 'S'
            SET @OutputString = STUFF(@OutputString, @Index + 1, 1,UPPER(SUBSTRING(@InputString, @Index + 1, 1)))
      END
         SET @Index = @Index + 1
      END

   RETURN ISNULL(@OutputString,'')

END
Go

--    00.045 x 0.045%
--    00.045 z 0.045%
--
--    Replace(Replace(Replace(TS_NAME,'x','#'),'z','x'),'#','z')
--
--    00.456 k 0.456%
--    00.511 j 0.511%
--    00.824 v 0.824%
--    kjv
--    Replace(Replace(Replace(Replace(TS_NAME,'k','#'),'j','k'),'v','j'),'#','v')
--
--    01.642 g 1.642%
--    02.284 n 2.284%
--    02.415 l 2.415%
--    gnl
--    Replace(Replace(Replace(Replace(TS_NAME,'g','#'),'n','g'),'l','n'),'#','l')
--
--    02.826 r 2.826%
--    03.174 d 3.174%
--    03.826 m 3.826%
--    rdm
--    Replace(Replace(Replace(Replace(TS_NAME,'r','#'),'d','r'),'m','d'),'#','m')
--
--    04.027 f 4.027%
--    04.200 h 4.200%
--    04.319 p 4.319%
--    04.434 b 4.434%
--    05.238 c 5.238%
--    fhpbc
--    Replace(Replace(Replace(Replace(Replace(Replace(TS_NAME,'f','#'),'h','f'),'p','h'),'b','p'),'c','b'),'#','c')
--
--    05.497 w 5.497%
--    06.686 s 6.686%
--    15.978 t 15.978%
--    wst
--    Replace(Replace(Replace(Replace(TS_NAME,'w','#'),'s','w'),'t','s'),'#','t')
--
--
--    02.799 e 2.799%
--    07.294 i 7.294%
--    07.631 o 7.631%
--    11.682 a 11.682%
--    eioa
--    Replace(Replace(Replace(Replace(Replace(TS_NAME,'e','#'),'i','ew'),'o','i'),'a','o'),'#','a')
--
--    -- dont replace
--    00.222 q 0.222%
--    00.763 y 0.763%
--    01.183 u 1.183%

-- Obfuscate a name
Select
   ts_id,
   Cast(ts_name as varchar(42)) as [Original Name]

   Cast(dbo.fnConvert_TitleCase(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(TS_NAME,'x','#'),'z','x'),'#','z'),'k','#'),'j','k'),'v','j'),'#','v'),'g','#'),'n','g'),'l','n'),'#','l'),'r','#'),'d','r'),'m','d'),'#','m'),'f','#'),'h','f'),'p','h'),'b','p'),'c','b'),'#','c'),'w','#'),'s','w'),'t','s'),'#','t'),'e','#'),'i','ew'),'o','i'),'a','o'),'#','a')) as VarChar(42)) As [morphed name] ,
   Cast(dbo.fnConvert_TitleCase(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(TS_NAME,'e','t'),'~','e'),'t','~'),'a','o'),'~','a'),'o','~'),'i','n'),'~','i'),'n','~'),'s','h'),'~','s'),'h','r'),'r','~'),'d','l'),'~','d'),'l','~'),'m','w'),'~','m'),'w','f'),'f','~'),'g','y'),'~','g'),'y','p'),'p','~'),'b','v'),'~','b'),'v','k'),'k','~'),'x','~'),'j','x'),'~','j')) as VarChar(42)) As [morphed name2]

From
   ts_users
;
于 2019-02-25T23:39:42.683 に答える
1

代わりに一時テーブルを使用すると、クエリが非常に高速になります。4 秒で 60K 行を実行しました。今後はこちらを使用します。

DECLARE TABLE #Names 
(Id int IDENTITY(1,1),[Name] varchar(100))

/* 苗字をごちゃまぜにする (ランダムに別の苗字を選ぶ) */

INSERT #Names
  SELECT LastName 
  FROM Customer 
  ORDER BY NEWID();
WITH [Customer ORDERED BY ROWID] AS
(SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS ROWID, LastName FROM Customer)

UPDATE [Customer ORDERED BY ROWID] 

SET LastName=(SELECT [Name] FROM #Names WHERE ROWID=Id)

DROP TABLE #Names
于 2009-04-30T15:12:16.263 に答える
1

ある種のランダムネームジェネレーターを使用しないのはなぜですか?

于 2008-10-03T21:05:40.147 に答える
1

リバーシブルな ROT47 とランダムな ROT47 を使用する方法を次に示します。どちらかに PK を追加して、「スクランブルされていない」バージョンにリンクすることができます

declare @table table (ID int, PLAIN_TEXT nvarchar(4000))
insert into @table
values
(1,N'Some Dudes name'),
(2,N'Another Person Name'),
(3,N'Yet Another Name')

--split your string into a column, and compute the decimal value (N) 
if object_id('tempdb..#staging') is not null drop table #staging
select 
    substring(a.b, v.number+1, 1) as Val
    ,ascii(substring(a.b, v.number+1, 1)) as N
    --,dense_rank() over (order by b) as RN
    ,a.ID
into #staging
from (select PLAIN_TEXT b, ID FROM @table) a
    inner join
         master..spt_values v on v.number < len(a.b)
where v.type = 'P' 

--select * from #staging


--create a fast tally table of numbers to be used to build the ROT-47 table.

;WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )



--Here we put it all together with stuff and FOR XML
select 
    PLAIN_TEXT
    ,ENCRYPTED_TEXT =
        stuff((
        select
            --s.Val
            --,s.N
            e.ENCRYPTED_TEXT
        from #staging s
        left join(
        select 
            N as DECIMAL_VALUE
            ,char(N) as ASCII_VALUE
            ,case 
                when 47 + N <= 126 then char(47 + N)
                when 47 + N > 126 then char(N-47)
            end as ENCRYPTED_TEXT
        from cteTally
        where N between 33 and 126) e on e.DECIMAL_VALUE = s.N
        where s.ID = t.ID
        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 0, '')
from @table t


--or if you want really random
select 
    PLAIN_TEXT
    ,ENCRYPTED_TEXT =
        stuff((
        select
            --s.Val
            --,s.N
            e.ENCRYPTED_TEXT
         from #staging s
        left join(
        select 
            N as DECIMAL_VALUE
            ,char(N) as ASCII_VALUE
            ,char((select ROUND(((122 - N -1) * RAND() + N), 0))) as ENCRYPTED_TEXT
        from cteTally
        where (N between 65 and 122) and N not in (91,92,93,94,95,96)) e on e.DECIMAL_VALUE = s.N
        where s.ID = t.ID
        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 0, '')
from @table t
于 2017-06-30T19:20:13.850 に答える
0

率直に言って、なぜこれが必要なのかわかりません。開発/テスト環境はプライベートで、ファイアウォールの背後にあり、Web からアクセスできないようにする必要があります。

あなたの開発者は信頼されるべきであり、彼らがあなたの信頼に応えられなかった場合、あなたは彼らに対して法的手段を講じることができます.

本当の質問は「データをスクランブルする必要がありますか?」であり、答えは (私の考えでは) 「いいえ」です。

何らかの理由でオフサイトに送信する場合、または環境に Web アクセスできるようにする必要がある場合、または偏執的である場合は、ランダム スイッチを実装します。一時テーブルを作成するのではなく、各場所とテーブル内のランダムな行の間でスイッチを実行し、一度に 1 つのデータを交換します。

最終結果は、すべて同じデータを含むテーブルになりますが、ランダムに再編成されます。また、一時テーブルよりも高速になるはずです。

Fisher-Yates Shuffleを SQL で実装するのに十分なほど単純である必要があります... または少なくとも、db を読み取ってターゲットに書き込むコンソール アプリで。

編集(2):T-SQLでのカフの答え:

declare @name varchar(50) set @name = (SELECT lastName from person where personID = (ランダムな ID 番号) Update person set lastname = @name WHERE personID = (現在の行の個人 ID)

これをループでラップし、Fisher-Yates のガイドラインに従ってランダム値の制約を変更すると、設定が完了します。

于 2008-10-03T21:08:12.120 に答える
0

私は今、会社でこれに取り組んでいますが、非常に難しいことがわかりました。現実的な名前を付けたいと考えていますが、実際の個人情報を明らかにしてはなりません。

私のアプローチは、最初に姓から他の姓へのランダム化された「マッピング」を作成し、次にそのマッピングを使用してすべての姓を変更することでした。これは、名前レコードが重複している場合に適しています。同じ実在の人物を表す 2 つの "John Smith" レコードがあるとします。1 つのレコードを "John Adams" に変更し、もう 1 つのレコードを "John Best" に変更した場合、1 つの "人" に 2 つの異なる名前が付けられます。マッピングを使用すると、「Smith」のすべての出現が「Jones」に変更されるため、重複 (または家族のメンバーでさえも) は同じ姓になり、データをより「現実的」に保ちます。

また、住所、電話番号、銀行口座番号などをごちゃまぜにする必要があります...そして、それらにどのようにアプローチすればよいかわかりません. スクランブリング中にデータを「現実的」に保つことは、確かに深いトピックです。これは多くの企業によって何度も行われたに違いありません。何を学びましたか?

于 2011-10-28T20:21:20.363 に答える