【问题标题】:Update a SQL table with random values from another table (no join condition)使用另一个表中的随机值更新 SQL 表(无连接条件)
【发布时间】:2013-01-21 10:21:58
【问题描述】:

我正在编写一个脚本来匿名化包含患者数据的表格。 我生成了一个包含 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# 并且只是编写该死的东西......

【问题讨论】:

  • @MahmoudGamal - 没有。应该是随机的。 @弗兰克你需要确保子查询是相关的例子这里stackoverflow.com/a/12922951/73226虽然只更新一列。如果您为所涉及的两个表都提供了CREATE TABLE,我可能会考虑修改它。
  • @MartinSmith - 非常酷。它确实只更新了一列。我的兴趣被激发了,虽然我很高兴为每一列调用这个 SQL(毕竟它是随机数据),但我很好奇如何在每个查询的一行中处理它。为什么需要我的 create 语句?我不需要更改数据库。我会吗?
  • 您使用的是什么 DBMS?如果您可以使用CROSS APPLY,您可以在链接中使用@MartinSmith 的回答相同的原则,使用UPDATE Patients SET Patients.pat_FirstName = fn.GivenName,.... FROM Patients CROSS APPLY (SELECT TOP 1 .... ORDER BY NEWID(), Patients.PatientID) fn
  • @GarethD - 来自tablesampleTOP 很可能是SQL Server,所以你的评论应该是一个答案。
  • 好吧,19 行并没有什么特别之处。这种行为是否始终可重复并出现在整个表格中?如果您发布实际执行计划的 XML,我可以看一下。

标签: sql random common-table-expression


【解决方案1】:

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

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

CREATE UNIQUE NONCLUSTERED INDEX ix ON FakeNameGenerator(ID)

那么就变成了生成1到50000之间的随机数的问题

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以继续寻找。

【讨论】:

  • 老实说,我认为每次阅读您的一个答案时,我至少学到了一件新东西!
  • 我已经将它调整到我的表格中,如下所示:UPDATE Patients SET 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 INNER LOOP JOIN FakeNameGenerator fn ON fn.ID = (1 + abs(CRYPT_GEN_RANDOM(8)%50000)),但结果与我开始的地方相同(所有行中的值相同)。如果您愿意,我可以为 Patient 和 FakeNameGenerator 发送CREATE SCRIPT 吗?我假设您不需要其他任何东西。
  • @FrankKaaijk - 实际执行计划的 XML 是我唯一有兴趣看到的。
  • @MartinSmith 我尝试使用 4000 行随机名称表更新 500.000 行表中的名字/姓氏。发生的情况是,大约 7-8 个名称最终被使用了 20.000 次,而其余的名称被使用了 70-80 次。我使用where P.FirstName = 'John' 为其中一个重复 20.000 次的名称重新运行它,它会选择另一个随机名称 20.000 次。所有这些约翰都变成了迈克。只有在重复几次之后,查询才会用随机名称替换 Mike。然后,我必须对其余被选中的名称重复 20.000 个。知道发生了什么吗?
  • @artm 并非没有看到确切的查询和计划。该计划是否有线轴?
【解决方案2】:

关于在子查询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

【讨论】:

  • 这假设 FakeNameGenerator 具有 >= 的行数 Patients 并且以不同的方式随机(例如洗牌而不是掷骰子),因为一旦从FakeNameGenerator 不能再次被选中。
  • @GarethD - MartinSmith 的假设不适用,随机列表几乎比实际患者行数短 10 倍。所以当我使用这个答案时,它仍然会产生与我第一次尝试相同的结果。它选择一次“随机”数据并用它填充完整的患者表。我会去阅读关于随机子查询的问答。
  • @MartinSmith 公平点,我做了无数次测试,无法通过交叉应用获得正确的行为,但事实证明这是因为我没有使用表中的主键来更新的顺序。我已经恢复到原来的解决方案。
  • 我使用 order by newid 来随机排序,虽然 TABLESAMPLE 这样做它只会为整个更新创建一个表样本,使用 ORDER BY NEWID(),PatientID 强制子查询重新评估每个排。您确定 NEWID 是错误的原因吗?该错误听起来与 NEWID 没有任何关系,听起来您正在尝试使用对于其数据类型而言太大的内容来更新列。
  • newid()tablesample 一起使用是个好主意,否则TOP 1 将始终为您提供页面的第一行。它只是选择随机页面,而不是随机化结果。
【解决方案3】:

另外一个想法是,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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多