使用另一个表中的随机值更新SQL表(无连接条件)

时间:2013-02-06 09:17:25

标签: sql random common-table-expression

我正在编写一个脚本来匿名化患者数据表。 我生成了一个包含50,000行匿名数据的表。

我需要的是患者表中的多个列要使用生成的表中的数据进行更新。

当然我已经阅读过有关更新表格以及如何从另一个表格中选择随机行的内容。我无法弄清楚的是如何在一个查询中将它结合起来。

我已经看到CTE是一种可能的解决方案,但我不明白它是如何工作的。我遇到的一个主要问题是生成的表中没有任何密钥,即使它确实存在也不应该是相关的,因为我只想迭代患者表的所有行,用随机行更新其值从生成的表格中。

我有以下内容:

Update Patients
Set Patients.pat_FirstName = fn.GivenName,
     pat_LastName = fn.SurName, 
     pat_StreetName = fn.StreetAddress,
     pat_PostalCode = fn.ZipCode,
     pat_City = fn.City,
     pat_DateOfBirth = fn.BirthDay,
from
     ( Select Top 1,
     GivenName, 
     SurName,
     StreetAddress,
     ZipCode,
     City,
     Birthday
from FakeNameGenerator tablesample(1000 rows)) as fn


但只执行'随机'一次,用相同的值填充患者表中的每一行。就像我之前说的那样,它可以(应该是)用CTE(计数?)表来解决,但是如何?

我已经接近抓住C#而只是编写了令人难忘的事情......

3 个答案:

答案 0 :(得分:3)

另一种方法是在FakeNameGenerator表中添加一个连续的数字列

ALTER TABLE FakeNameGenerator ADD ID INT NOT NULL IDENTITY(1,1)

CREATE UNIQUE NONCLUSTERED INDEX ix ON FakeNameGenerator(ID)

然后它就成了生成1到50,000之间随机数的问题

UPDATE P
SET P.pat_FirstName = F.GivenName /*...*/
FROM Patients P
INNER LOOP JOIN FakeNameGenerator F ON F.ID = (1 +  ABS(CRYPT_GEN_RANDOM(8)%50000))

INNER LOOP JOIN提示强制使用Patients作为驱动表的嵌套循环连接。它会针对每一行重新评估要FakeNameGenerator进行搜索而寻找ID

答案 1 :(得分:1)

在子查询here中使用随机排序有更详细的问答。但总之,我无法通过从随机数据中选择前1来找到一种方法来实现这一点。我给了源和样本数据一个随机id(使用ROW_NUMBER)然后加入两个以获得随机更新:

WITH PatientCTE AS
(   SELECT  PatientID,
            pat_FirstName,
            pat_LastName,
            pat_StreetName,
            pat_PostalCode,
            pat_City,
            pat_DateOfBirth,
            rn = ROW_NUMBER() OVER(ORDER BY NEWID())
    FROM    Patients
), SampleData AS
(   SELECT  GivenName, 
            SurName, 
            StreetAddress, 
            ZipCode, 
            City, 
            Birthday,
            rn = ROW_NUMBER() OVER(ORDER BY NEWID())
    FROM    FakeNameGenerator
)
UPDATE  PatientCTE
SET     Patients.pat_FirstName = fn.GivenName,
        pat_LastName = fn.SurName, 
        pat_StreetName = fn.StreetAddress,
        pat_PostalCode = fn.ZipCode,
        pat_City = fn.City,
        pat_DateOfBirth = fn.BirthDay,
FROM    PatientCTE p
        INNER JOIN SampleData fn
            ON fn.rn = p.rn

修改

好的,所以看来我的测试没有可比性,我原来的想法会起作用。没有DDL和数据进行测试我无法确定,但这应该有效:

UPDATE  Patients
SET     Patients.pat_FirstName = fn.GivenName,
        pat_LastName = fn.SurName, 
        pat_StreetName = fn.StreetAddress,
        pat_PostalCode = fn.ZipCode,
        pat_City = fn.City,
        pat_DateOfBirth = fn.BirthDay,
FROM    Patients
        CROSS APPLY
        (   SELECT  TOP 1 
                    GivenName, 
                    SurName, 
                    StreetAddress, 
                    ZipCode, 
                    City, 
                    Birthday
            FROM    FakeNameGenerator TABLESAMPLE(1000 ROWS)
            ORDER BY NEWID(), Patients.Patient_ID
        ) fn

答案 2 :(得分:0)

另外一个想法是,GarethD方法的问题是,它需要第二个表中的行数与第一个表中的行数相同或更多。

因此,您可以将第二个表与第一个表进行交叉连接,并将结果限制为第一个表中的行数。

WITH PatientCTE AS
(
    SELECT  
         PatientID 
        ,pat_FirstName 
        ,pat_LastName 
        ,pat_StreetName 
        ,pat_PostalCode 
        ,pat_City 
        ,pat_DateOfBirth 
        ,rn = ROW_NUMBER() OVER(ORDER BY NEWID()) 
    FROM Patients
)
, SampleData AS
(
    SELECT TOP (SELECT COUNT(*) FROM PatientCTE )  
             GivenName 
            ,SurName 
            ,StreetAddress 
            ,ZipCode 
            ,City 
            ,Birthday 
            ,rn = ROW_NUMBER() OVER(ORDER BY NEWID())
    FROM FakeNameGenerator 

    CROSS JOIN PatientCTE 
)

UPDATE p
SET      p.pat_FirstName = fn.GivenName 
        ,p.pat_LastName = fn.SurName 
        ,p.pat_StreetName = fn.StreetAddress 
        ,p.pat_PostalCode = fn.ZipCode 
        ,p.pat_City = fn.City 
        ,p.pat_DateOfBirth = fn.BirthDay 
FROM PatientCTE AS p

INNER JOIN SampleData AS fn
    ON fn.rn = p.rn