从多个表中选择同一字段

Selecting the same field from multiple tables

提问人:Djaq Harris 提问时间:6/22/2023 最后编辑:Djaq Harris 更新时间:6/28/2023 访问量:60

问:

我正在编写一个查询来复制 Dynamics NAV 的数据库以创建测试环境。我要解决的难点是,一旦创建了新的测试环境,我就需要从某些字段中清除信息,以便测试环境不会处理实时数据。

NAV 在数据库中设置表的方式是使用 [Company_Name$Table_Name] 格式。[Field],在较新的版本中,它会在表名的末尾添加一个数字序列。

我正在尝试在我的脚本中添加一个部分,该部分将动态浏览表以查找表名称的所有实例并删除该表上指定字段中的数据。表名本身是常量,并且具有该表名的每个表中的字段都是常量。

到目前为止,我尝试作为查找数据的测试是嵌套的 select 语句来获取这些表中的记录列表。我可以使用以下方法获取表名:

select *
from sys.tables
where name like '%table_name%

我不知道/理解的是如何将该信息传递到以下子查询中:

Select [Profile]from [table_name] where [field] is not null

我尝试做的是以下操作,以获取一般的数据,但是当我这样做时,它说语法最后不正确,我不确定:

Select *
From(select [name]
    From sys.tables
    where name like '%tableName%'
)

我尝试的最后一件事是尝试将表名放在字段名称的前面,但这也没有用

select (select [name] 
    from sys.tables 
    where name like '%Interface Profile').[Profile] 
from (
    select [name] 
    from sys.tables 
    where name like '%Interface Profile') as profiles

============================================================================== 更新

根据 @siggermannen 的建议,我想出了以下代码

DECLARE @companylist TABLE (name_like nvarchar(128),field SYSNAME, field_value_to_set nvarchar(max))
INSERT INTO @companylist (
                name_like, 
                field,
                field_value_to_set)
                Values ('%Interface Profile%','Path',null)
                ,   ('%Interface Profile%','Archive Path',null)
                ,   ('%Interface Profile%','Import Error Path',null)
                ,   ('%PW Setup%','Communication PDF Path',null)
                ,   ('%PW Trx Activity%','Document Path',null)
                ,   ('%TPL Document Index Import%','Journal Importpath',null)
                ,   ('%TPL Document Index Import%','Exportpath',null)
                ,   ('%E_D_I_ Template%','Interface File Path',null)
                ,   ('%E_D_I_ Setup%','Common Receive Path',null)
                ,   ('%E_D_I_ Setup%','Common Work Path',null)
                ,   ('%PW Communication Rule%','To Email Address',null)
                ,   ('%PW Communication Rule%','CC Email Address',null)
                ,   ('%PW Communication Rule%','BCC Email Address',null)

DECLARE @SQL NVARCHAR(MAX)
,   @name SYSNAME
,   @field SYSNAME, @field_value_to_set NVARCHAR(MAX)
,   @start INT
,   @end INT

DECLARE CR_X CURSOR READ_ONLY FORWARD_ONLY LOCAL STATIC FOR
    SELECT  t.name
    ,   field
    ,   field_value_to_set
    ,   ROW_NUMBER() OVER(PARTITION BY name_like ORDER BY id) AS sort
    ,   ROW_NUMBER() OVER(PARTITION BY name_like ORDER BY id DESC) AS sort_last
    FROM    @companylist fc
    INNER JOIN sys.tables t
        ON  t.name COLLATE DATABASE_DEFAULT LIKE fc.name_like COLLATE DATABASE_DEFAULT
    INNER JOIN sys.columns sc
        ON  sc.object_id = t.object_id
        AND sc.name COLLATE DATABASE_DEFAULT = fc.field COLLATE DATABASE_DEFAULT
    WHERE   t.is_ms_shipped = 0 -- just in case
    ORDER BY t.name

OPEN CR_X
WHILE 1 = 1
BEGIN
    FETCH NEXT FROM CR_X INTO @name, @field, @field_value_to_set,@start, @end

    IF @@FETCH_STATUS <> 0
        BREAK
    -- Build SQL update string
    IF @start = 1
        SET @SQL = 'UPDATE ' + QUOTENAME(@name) + N'
     SET '
     if @start > 1
      set @SQL = @SQL + ','

      set @SQL = @SQL + QUOTENAME(@field) + ' = ' + @field_value_to_set

    IF @end = 1 -- Energize!
    BEGIN
        PRINT @SQL
        EXEC(@SQL)
    END

END
CLOSE CR_X
DEALLOCATE CR_X

select [Communication PDF Path]
FROM [CRONUS 3PL DEMO 110$PW Setup]

--select *
--from anothertable
--Clean up sample data
--drop table tableName
--drop table anothertable

我目前遇到的地方是我在运行它时出现以下错误,但我不确定如何弄清楚 t 中有什么

Msg 207, Level 16, State 1, Line 31
Invalid column name 'id'.
Msg 207, Level 16, State 1, Line 32
Invalid column name 'id'.

Completion time: 2023-06-28T10:25:38.7999707-04:00

有什么想法吗?

sql-server 嵌套 通配符 多表

评论

2赞 Thom A 6/22/2023
如果我理解正确的话,你需要安全地构建动态SQL。我经常(但并非总是)发现这样的要求是由于设计缺陷造成的。考虑到你有这个名字的表格,听起来你的设计确实有缺陷;您应该有 1 个表,其中包含(或更有可能)列。我建议修复你的设计应该是你的首要任务。[Company_Name$Table_Name].[Field]CompanyNameCompanyID
1赞 T N 6/22/2023
您需要生成并执行动态 SQL。如果你搜索这个术语,你应该会找到很多例子。但是,如果这是一个您不经常执行的手动过程,那么“穷人”的解决方案可能更简单。只需编写一个生成所需命令的选择,然后在新窗口中粘贴、查看和执行它们。像.根据您的需求进行定制。SELECT 'SELECT * FROM ' + QUOTENAME(t.name) AS sql from sys.tables t where t.name like '%Interface Profile'

答:

-1赞 Bill Smith 6/22/2023 #1

您必须在 sql server 中为内联视图添加别名。例如,这有效

Select *
From(select [name]
    From sys.tables
    where name like '%tableName%'
) as x

评论

0赞 T N 6/22/2023
这不是如何操纵(针对)匹配表运行 DML 这一主要问题的答案。
0赞 siggemannen 6/22/2023 #2

下面是一个您可以使用的通用解决方案:


-- Create some sample data
create table tableName (field1 INT, field2 INT)

insert into tableName values (1, 2), (3,4)

create table anothertable(field1 INT, field2 varchar(30), field3 NVARCHAR(300))

insert into anothertable (field1, field2, field3)
select 1, 'test', 'another test'


declare @fields_to_clear table (
    name_like NVARCHAR(100)
,   field SYSNAME
,   field_value_to_set NVARCHAR(MAX)
,   id int identity
)

-- Here are the names and fields you want cleared
INSERT INTO @fields_to_clear (
    name_like, field, field_value_to_set
)
VALUES  ('%tableName%', 'field1', 'NULL')
,   ('%anotherTable%', 'field2', 'NULL')
,   ('%anotherTable%', 'field3', 'NULL')

DECLARE @SQL NVARCHAR(MAX)
,   @name SYSNAME
,   @field SYSNAME, @field_value_to_set NVARCHAR(MAX)
,   @start INT
,   @end INT

-- Get all matching tables and fields
DECLARE CR_X CURSOR READ_ONLY FORWARD_ONLY LOCAL STATIC FOR
    SELECT  t.name
    ,   field
    ,   field_value_to_set
    ,   ROW_NUMBER() OVER(PARTITION BY name_like ORDER BY id) AS sort
    ,   ROW_NUMBER() OVER(PARTITION BY name_like ORDER BY id DESC) AS sort_last
    FROM    @fields_to_clear fc
    INNER JOIN sys.tables t
        ON  t.name COLLATE DATABASE_DEFAULT LIKE fc.name_like COLLATE DATABASE_DEFAULT
    INNER JOIN sys.columns sc
        ON  sc.object_id = t.object_id
        AND sc.name COLLATE DATABASE_DEFAULT = fc.field COLLATE DATABASE_DEFAULT
    WHERE   t.is_ms_shipped = 0 -- just in case
    ORDER BY t.name, sort

OPEN CR_X
WHILE 1 = 1
BEGIN
    FETCH NEXT FROM CR_X INTO @name, @field, @field_value_to_set,@start, @end

    IF @@FETCH_STATUS <> 0
        BREAK
    -- Build SQL update string
    IF @start = 1
        SET @SQL = 'UPDATE ' + QUOTENAME(@name) + N'
     SET '
     if @start > 1
      set @SQL = @SQL + ','

      set @SQL = @SQL + QUOTENAME(@field) + ' = ' + @field_value_to_set

    IF @end = 1 -- Energize!
    BEGIN
        PRINT @SQL
        EXEC(@SQL)
    END

END
CLOSE CR_X
DEALLOCATE CR_X

select *
from tableName

select *
from anothertable
--Clean up sample data
--drop table tableName
--drop table anothertable

我创建了一个小小的基础表,您可以填充表名 LIKE 和要清除的字段名称,最后填充要设置的值。我使用 NULL,但从理论上讲,您也可以将它们设置为 0 或其他任何值。请注意,由于动态 SQL 的性质,字符串可能需要额外的引号。@fields_to_clear

然后我创建一个游标,用于在系统目录中查找匹配的表并逐个循环字段。该循环构建动态 SQL,该 SQL 将每个字段设置为列中的值。sys.tablesfield_value_to_set

您可以注释 EXEC(@SQL) 以查看正在生成的实际 SQL。

小心 like-string,这样你就不会吹走你的整个数据库