循环遍历表和字段列表并将它们混合

Posted

技术标签:

【中文标题】循环遍历表和字段列表并将它们混合【英文标题】:loop over a list of tables and field and mix them 【发布时间】:2019-03-10 09:47:00 【问题描述】:

我正在寻找一种有效的方法来覆盖需要混合的给定表和字段列表。

我想这应该变成一个函数或存储过程。

输入应该是某种表名列表,每个表应该有另一个列表,其中包含唯一索引列、键列以及所有其他需要混合的列。

算法和给定代码解释如下:

首先,混合是指保留每列中的所有值,但以不同的顺序在行之间重新分配它们。

SQL 语法:

declare @tablename varchar, @keyColumn varchar, @ColumnForBase  ,@ColumnToMix 

update [@tablename] 
set [@tablename].[@ColumnToMix]=c.[@ColumnToMix],[@tablename].[@ColumnForBase]=c.[@ColumnForBase]
from [@tablename] left join
(
    SELECT a.[@ColumnToMix] as [@ColumnToMix] ,b.[@ColumnForBase] as [@ColumnForBase],b.[@keyColumn] as [@keyColumn]
    FROM
        (SELECT row_number() OVER (ORDER BY [@ColumnToMix]) num, [@ColumnToMix]
        FROM [@tablename]) as a 
        left join
        (SELECT row_number() OVER (ORDER BY [@ColumnForBase]) num, [@keyColumn],[@ColumnForBase]
        FROM [@tablename] ) as b 
        ON a.num=b.num
)as c  ON c.[@keyColumn]=[@tablename].[@keyColumn]

说明和例子:

假设我有一个包含 4 列的表:索引、ID、名称、地址 该算法对 ID 和名称重新排序,在每一行添加一个数字。 由于行数相同,我可以通过行号连接两个重新排序的列,然后更新原始表 - 将一列 (ColumnToMix) 更改为重新分配的值。 假设原始表名为“People”,如下所示:

Index Id   Name   Address 
1     52   Jill   New-York
2     57   John   Chicago
5     63   Bill   Alabama

变量是 @tablename = 人,@keyColumn = 索引,@ColumnForBase = Id,@ColumnToMix = 名称

上面的代码运行结果是

Index Id   Name   Address 
1     52   Bill   New-York
2     57   Jill   Chicago
5     63   John   Alabama

现在名字混在一起了。

为了混合多于一列,代码需要能够遍历所有必要的字段。

有什么想法吗?

【问题讨论】:

这似乎是一个危险的想法......行是一个数据单元,像你描述的那样混合数据库中的特定列(或列)意味着将数据弄乱到一个点将不再有意义并且可能不会回头(恢复较旧的备份除外)。你为什么要做这样的事情? 这个想法是有意混合数据以获得随机不同的数据集。它对 QA 有好处,也可用于将取自真实环境的数据打乱到测试环境中。 ...也可以用于(错误地或恶意地)将生产数据库更改为无法使用的程度,或者甚至可以破解它(在用户表中混合用户名或密码, 例如)。顺便说一句,如果您指定您正在使用的 rdbms(品牌和版本),您将更有可能获得答案 谢谢,添加了 rdbms 规范。关于用法 - 任何 DELETE 或 DROP TABLE 都可能同样或更危险,显然这应该谨慎使用...... 【参考方案1】:

好吧,对于一个硬编码的列来说,这是一项相当简单的任务:

UPDATE T0
SET Name = T1.Name
FROM 
(
    SELECT Name, ROW_NUMBER() OVER(ORDER BY OriginalOrderColumn) As RN
    FROM Table
) AS T0
JOIN
(
    SELECT Name, ROW_NUMBER() OVER(ORDER BY NEWID()) As RN
    FROM Table
) AS T1
ON T0.RN = T1.RN

*OriginalOrderColumn 表示代表“自然”顺序的列 - 如标识列或创建日期列。请注意,表中的记录没有自然顺序,因为表未按定义排序。

但为了参数化表名和列名,您需要在存储过程中使用动态 SQL:

CREATE PROCEDURE MixValuesInAColumn
(
    @TableName sysname, 
    @ColumnToMix sysname, 
    @OriginalOrderBy sysname
)
AS
-- White-listing table and column names
IF EXISTS
(
     SELECT 1 
     FROM Information_Schema.Columns
     WHERE TABLE_NAME = @TableName
     AND COLUMN_NAME = @ColumnToMix 
 ) AND EXISTS
(
     SELECT 1 
     FROM Information_Schema.Columns
     WHERE TABLE_NAME = @TableName
     AND COLUMN_NAME = @OriginalOrderBy 
 ) BEGIN

DECLARE @Sql nvarchar(max) = 
    'UPDATE T0 SET '+ QUOTENAME(@ColumnToMix) +' = T1.'+ QUOTENAME(@ColumnToMix) +' '+ 
    'FROM (
        SELECT '+ @ColumnToMix +', ROW_NUMBER() OVER(ORDER BY '+ QUOTENAME(@OriginalOrderBy) +') As [Original Order] 
        FROM '+ QUOTENAME(@TableName) + '
    ) As T0
    JOIN 
    (
        SELECT '+ QUOTENAME(@ColumnToMix) +', ROW_NUMBER() OVER(ORDER BY NEWID()) As [Random Order] 
        FROM '+ QUOTENAME(@TableName) + '
    ) AS T1 ON T0.[Original Order] = T1.[Random Order]'

EXEC(@SQL)

END

我建议不要在一次执行中混合多个列,因为它们都会以相同的随机顺序混合 - 但是您可以创建另一个过程来获取所需的参数(表名、主键和它是要混合的列)并分别为每列运行此过程。

You can see a live demo on rextester(基于您提供的示例数据)。

【讨论】:

您需要在此处验证或引用对象。 @ColumnToMix 可以很容易地在这里注入。 @Larnu 你说得对,我也想过要提一下——但话又说回来,如果这个过程是由恶意的人执行的,即使不使用 SQL 也会对数据库造成真正的损害注射。顺便说一句,实际上这个过程的所有参数都是正确的。 @Larnu 我添加了白名单测试。 这肯定会让事情变得更安全。无论如何,我都会添加QUOTENAME,但如果 OP 有任何带有空格、右括号、以数字开头等字符的对象,则更是如此。上帝禁止 OP 有一个名为 sys.objects]; DROP TABLE MyTable;-- 的对象,但是如果他们能以某种方式首先创建它,恶意的人可能会尝试它。此外,从nvarchar(4000) 更改为sysname 是正确的选择。 :) Glad to help :-)

以上是关于循环遍历表和字段列表并将它们混合的主要内容,如果未能解决你的问题,请参考以下文章

循环遍历一个大数组并将值加在一起 ​​C#

Django:遍历模板中的过滤列表

Odoo - 遍历字段,获取值并将它们放入新字段

循环遍历两个字符串数组并将它们设置为对象属性

哪个更适合tkinter网格,放置单个项目或循环遍历列表来放置它们?

循环遍历对象数组,并将它们转换为 UL 中的 LI 项 [重复]