Rom*_*kar 22 sql t-sql sql-server sql-server-2016
我已经就此事做了一些研究,但还没有解决方案.我想得到的是视图中的列级依赖项.所以,假设我们有一个这样的表
create table TEST(
first_name varchar(10),
last_name varchar(10),
street varchar(10),
number int
)
Run Code Online (Sandbox Code Playgroud)
和这样的观点:
create view vTEST
as
select
first_name + ' ' + last_name as [name],
street + ' ' + cast(number as varchar(max)) as [address]
from dbo.TEST
Run Code Online (Sandbox Code Playgroud)
我想要的是得到这样的结果:
column_name depends_on_column_name depends_on_table_name
----------- --------------------- --------------------
name first_name dbo.TEST
name last_name dbo.TEST
address street dbo.TEST
address number dbo.TEST
Run Code Online (Sandbox Code Playgroud)
我已尝试过sys.dm_sql_referenced_entities功能,但referencing_minor_id视图总是为0.
select
referencing_minor_id,
referenced_schema_name + '.' + referenced_entity_name as depends_on_table_name,
referenced_minor_name as depends_on_column_name
from sys.dm_sql_referenced_entities('dbo.vTEST', 'OBJECT')
referencing_minor_id depends_on_table_name depends_on_column_name
-------------------- --------------------- ----------------------
0 dbo.TEST NULL
0 dbo.TEST first_name
0 dbo.TEST last_name
0 dbo.TEST street
0 dbo.TEST number
Run Code Online (Sandbox Code Playgroud)
sys.sql_expression_dependencies过时也是如此sys.sql_dependencies.
我错过了什么或不可能做到的?
有一些相关的问题(查找视图中使用的别名的真实列名?),但正如我所说 - 我还没有找到一个有效的解决方案.
编辑1:我试图使用DAC来查询此信息是否存储在系统基表中的某个位置但未找到它
此解决方案只能部分回答您的问题.它不适用于表达式的列.
您可以使用sys.dm_exec_describe_first_result_set来获取列信息:
@include_browse_information
如果设置为1,则分析每个查询,就好像它在查询上有FOR BROWSE选项一样.返回其他键列和源表信息.
CREATE TABLE txu(id INT, first_name VARCHAR(10), last_name VARCHAR(10));
CREATE TABLE txd(id INT, id_fk INT, address VARCHAR(100));
CREATE VIEW v_txu
AS
SELECT t.id AS PK_id,
t.first_name AS name,
d.address,
t.first_name + t.last_name AS name_full
FROM txu t
JOIN txd d
ON t.id = d.id_fk
Run Code Online (Sandbox Code Playgroud)
主要查询:
SELECT name, source_database, source_schema,
source_table, source_column
FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM v_txu', null, 1) ;
Run Code Online (Sandbox Code Playgroud)
输出:
+-----------+--------------------+---------------+--------------+---------------+
| name | source_database | source_schema | source_table | source_column |
+-----------+--------------------+---------------+--------------+---------------+
| PK_id | fiddle_0f9d47226c4 | dbo | txu | id |
| name | fiddle_0f9d47226c4 | dbo | txu | first_name |
| address | fiddle_0f9d47226c4 | dbo | txd | address |
| name_full | null | null | null | null |
+-----------+--------------------+---------------+--------------+---------------+
Run Code Online (Sandbox Code Playgroud)
小智 5
它是基于查询计划的解决方案.它有一些冒险
和不满
核心思想是XML查询计划中的每个列表达式都在"DefinedValue"节点中定义."DefinedValue"的第一个子节点是对输出列的引用,第二个是表达式.表达式根据输入列和常量值计算.如上所述,它仅基于经验观察,需要进行适当的测试.
这是一个调用示例:
exec dbo.GetColumnDependencies 'select * from dbo.vTEST'
target_column_name | source_column_name | const_value
---------------------------------------------------
address | Expr1007 | NULL
name | Expr1006 | NULL
Expr1006 | NULL | ' '
Expr1006 | [testdb].[dbo].first_name | NULL
Expr1006 | [testdb].[dbo].last_name | NULL
Expr1007 | NULL | ' '
Expr1007 | [testdb].[dbo].number | NULL
Expr1007 | [testdb].[dbo].street | NULL
Run Code Online (Sandbox Code Playgroud)
这是代码.首先得到XML查询计划.
declare @select_query as varchar(4000) = 'select * from dbo.vTEST' -- IT'S YOUR QUERY HERE.
declare @select_into_query as varchar(4000) = 'select top (1) * into #foo from (' + @select_query + ') as src'
, @xml_plan as xml = null
, @xml_generation_tries as tinyint = 10
;
while (@xml_plan is null and @xml_generation_tries > 0) -- There is no guaranty that plan will be cached.
begin
execute (@select_into_query);
select @xml_plan = pln.query_plan
from sys.dm_exec_query_stats as qry
cross apply sys.dm_exec_sql_text(qry.sql_handle) as txt
cross apply sys.dm_exec_query_plan(qry.plan_handle) as pln
where txt.text = @select_into_query
;
end
if (@xml_plan is null
) begin
raiserror(N'Can''t extract XML query plan from cache.' ,15 ,0);
return;
end
;
Run Code Online (Sandbox Code Playgroud)
接下来是一个主要查询.它最大的部分是用于列提取的递归公用表表达式.
with xmlnamespaces(default 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'
,'http://schemas.microsoft.com/sqlserver/2004/07/showplan' as shp -- Used in .query() for predictive namespace using.
)
, cte_column_dependencies as
(
Run Code Online (Sandbox Code Playgroud)
递归的种子是一个查询,它提取存储1行感兴趣的选择查询的#foo表的列.
select
(select foo_col.info.query('./ColumnReference') for xml raw('shp:root') ,type) -- Becouse .value() can't extract attribute from root node.
as target_column_info
, (select foo_col.info.query('./ScalarOperator/Identifier/ColumnReference') for xml raw('shp:root') ,type)
as source_column_info
, cast(null as xml) as const_info
, 1 as iteration_no
from @xml_plan.nodes('//Update/SetPredicate/ScalarOperator/ScalarExpressionList/ScalarOperator/MultipleAssign/Assign')
as foo_col(info)
where foo_col.info.exist('./ColumnReference[@Table="[#foo]"]') = 1
Run Code Online (Sandbox Code Playgroud)
递归部分使用依赖列搜索"DefinedValue"节点,并提取列表达式中使用的所有"ColumnReference"和"Const"子节点.XML到SQL的转换过于复杂.
union all
select
(select internal_col.info.query('.') for xml raw('shp:root') ,type)
, source_info.column_info
, source_info.const_info
, prev_dependencies.iteration_no + 1
from @xml_plan.nodes('//DefinedValue/ColumnReference') as internal_col(info)
inner join cte_column_dependencies as prev_dependencies -- Filters by depended columns.
on prev_dependencies.source_column_info.value('(//ColumnReference/@Column)[1]' ,'nvarchar(4000)') = internal_col.info.value('(./@Column)[1]' ,'nvarchar(4000)')
and exists (select prev_dependencies.source_column_info.value('(.//@Schema)[1]' ,'nvarchar(4000)') intersect select internal_col.info.value('(./@Schema)[1]' ,'nvarchar(4000)'))
and exists (select prev_dependencies.source_column_info.value('(.//@Database)[1]' ,'nvarchar(4000)') intersect select internal_col.info.value('(./@Database)[1]' ,'nvarchar(4000)'))
and exists (select prev_dependencies.source_column_info.value('(.//@Server)[1]' ,'nvarchar(4000)') intersect select internal_col.info.value('(./@Server)[1]' ,'nvarchar(4000)'))
cross apply ( -- Becouse only column or only constant can be places in result row.
select (select source_col.info.query('.') for xml raw('shp:root') ,type) as column_info
, null as const_info
from internal_col.info.nodes('..//ColumnReference') as source_col(info)
union all
select null as column_info
, (select const.info.query('.') for xml raw('shp:root') ,type) as const_info
from internal_col.info.nodes('..//Const') as const(info)
) as source_info
where source_info.column_info is null
or (
-- Except same node selected by '..//ColumnReference' from its sources. Sorry, I'm not so well to check it with XQuery simple.
source_info.column_info.value('(//@Column)[1]' ,'nvarchar(4000)') <> internal_col.info.value('(./@Column)[1]' ,'nvarchar(4000)')
and (select source_info.column_info.value('(//@Schema)[1]' ,'nvarchar(4000)') intersect select internal_col.info.value('(./@Schema)[1]' ,'nvarchar(4000)')) is null
and (select source_info.column_info.value('(//@Database)[1]' ,'nvarchar(4000)') intersect select internal_col.info.value('(./@Database)[1]' ,'nvarchar(4000)')) is null
and (select source_info.column_info.value('(//@Server)[1]' ,'nvarchar(4000)') intersect select internal_col.info.value('(./@Server)[1]' ,'nvarchar(4000)')) is null
)
)
Run Code Online (Sandbox Code Playgroud)
最后,它是将XML转换为适当的人类文本的select语句.
select
-- col_dep.target_column_info
--, col_dep.source_column_info
--, col_dep.const_info
coalesce(col_dep.target_column_info.value('(.//shp:ColumnReference/@Server)[1]' ,'nvarchar(4000)') + '.' ,'')
+ coalesce(col_dep.target_column_info.value('(.//shp:ColumnReference/@Database)[1]' ,'nvarchar(4000)') + '.' ,'')
+ coalesce(col_dep.target_column_info.value('(.//shp:ColumnReference/@Schema)[1]' ,'nvarchar(4000)') + '.' ,'')
+ col_dep.target_column_info.value('(.//shp:ColumnReference/@Column)[1]' ,'nvarchar(4000)')
as target_column_name
, coalesce(col_dep.source_column_info.value('(.//shp:ColumnReference/@Server)[1]' ,'nvarchar(4000)') + '.' ,'')
+ coalesce(col_dep.source_column_info.value('(.//shp:ColumnReference/@Database)[1]' ,'nvarchar(4000)') + '.' ,'')
+ coalesce(col_dep.source_column_info.value('(.//shp:ColumnReference/@Schema)[1]' ,'nvarchar(4000)') + '.' ,'')
+ col_dep.source_column_info.value('(.//shp:ColumnReference/@Column)[1]' ,'nvarchar(4000)')
as source_column_name
, col_dep.const_info.value('(/shp:root/shp:Const/@ConstValue)[1]' ,'nvarchar(4000)')
as const_value
from cte_column_dependencies as col_dep
order by col_dep.iteration_no ,target_column_name ,source_column_name
option (maxrecursion 512) -- It's an assurance from infinite loop.
Run Code Online (Sandbox Code Playgroud)