提问人:Djaq Harris 提问时间:6/22/2023 最后编辑:Djaq Harris 更新时间:6/28/2023 访问量:60
从多个表中选择同一字段
Selecting the same field from multiple tables
问:
我正在编写一个查询来复制 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 中为内联视图添加别名。例如,这有效
Select *
From(select [name]
From sys.tables
where name like '%tableName%'
) as x
评论
下面是一个您可以使用的通用解决方案:
-- 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.tables
field_value_to_set
您可以注释 EXEC(@SQL) 以查看正在生成的实际 SQL。
小心 like-string,这样你就不会吹走你的整个数据库
评论
[Company_Name$Table_Name].[Field]
CompanyName
CompanyID
SELECT 'SELECT * FROM ' + QUOTENAME(t.name) AS sql from sys.tables t where t.name like '%Interface Profile'