【问题标题】:TSQL i need help by transforming sql statement in functionTSQL 我需要通过在函数中转换 sql 语句来获得帮助
【发布时间】:2022-01-17 16:08:24
【问题描述】:

通过以下语句我得到结果(经过小的更改 id、表名 ...)

declare @RowId int = 1 declare @TableName sysname = 'ParentTable'

declare @Command varchar(max) 

select @Command = isnull(@Command + ' union all ', '') + 'select ''' +
object_name(parent_object_id) + 
    ''' where exists(select * from ' + object_name(parent_object_id) + ' where ' + col.name+ ' = ' + cast(@RowId as varchar) + ')'  from
sys.foreign_key_columns fkc
    join sys.columns col on fkc.parent_object_id = col.object_id  and fkc.parent_column_id = col.column_id

where object_name(referenced_object_id) = @TableName

execute (@Command)

我想在 tsql 函数中转换这条语句

【问题讨论】:

  • 动态 SQL,@Stu 。动态 SQL 不能在函数中使用。期间。
  • 听起来像XY Problem,你想要一个“一刀切”的功能;这不是 SQL 的工作方式。根据需要编写 SQL;并且你不需要一个 do anything 功能。如果您需要像这样使用动态 SQL,则表明您存在潜在的设计缺陷。
  • @Larnu 我意识到 :) 可能不需要动态 sql 但没有解释要求,谁知道?
  • 我们都不是,那是肯定的@Stu。 :)

标签: sql database function tsql


【解决方案1】:

虽然不可能在函数中包含动态 SQL,但可以动态生成一个函数来处理给定架构的所有情况。

下面是一些可以生成静态函数定义的 SQL,其中包含检查所有外键的逻辑。生成后,可以根据需要使用此函数,直到架构更改。到那时,它就需要重新生成。

--  SQL to generate a function that will identify all tables having an
--  FK reference to a specific parent table and key value.
--  Assumptions:
--  1. All PKs are of the same specified type. For multiple types, separate functions may be generated.
--  2. Primary and foreign keys are single columns.

-- Settings
DECLARE @FunctionName sysname = 'fn_ForeignKeyReferences'
DECLARE @PrimaryKeyDataType sysname = 'int' -- int, uniqueidentifier, ... other type from sys.types

-- Additional generated settings
DECLARE @GeneratedDateTime NVARCHAR(50) = CONVERT(NVARCHAR(50), GETUTCDATE(), 20)
DECLARE @NoTablesFoundMessage NVARCHAR(100) = '-- No tables with specified PK type "' + @PrimaryKeyDataType + '" were found'
DECLARE @CharKeyTypeSuffix NVARCHAR(10) = CASE WHEN @PrimaryKeyDataType LIKE '%char%' THEN '(max)' ELSE '' END

-- Table to hold FK table and column data
DECLARE @FkInfo TABLE (
    ForeignKeyName sysname,
    ParentSchemaName sysname, ParentTableName sysname, ParentColumnName sysname,
    ChildSchemaName sysname, ChildTableName sysname, ChildColumnName sysname
)

-- Gather FK information
INSERT INTO @FkInfo
SELECT fk.name, ps.name, pt.name, pc.name, cs.name, ct.name, cc.name
FROM sys.foreign_keys fk
JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
-- parent (primary key) schema/table/column
JOIN sys.tables pt ON pt.object_id = fkc.referenced_object_id
JOIN sys.schemas ps ON ps.schema_id = pt.schema_id
JOIN sys.columns pc
    on pc.object_id = fkc.referenced_object_id
    and pc.column_id = fkc.referenced_column_id
-- Chile (foreign key) schema/table/column
JOIN sys.tables ct ON ct.object_id = fkc.parent_object_id
JOIN sys.schemas cs ON cs.schema_id = ct.schema_id
JOIN sys.columns cc
    on cc.object_id = fkc.parent_object_id
    and cc.column_id = fkc.parent_column_id
JOIN sys.types t ON t.user_type_id = pc.user_type_id
WHERE t.name = @PrimaryKeyDataType
ORDER BY ps.name, pt.name, pc.name, cs.name, ct.name, cc.name

-- Diagnostic preview
--SELECT * FROM @FkInfo

-- SQL templates (Drop function)
DECLARE @DropFunctionTemplate NVARCHAR(MAX) = '
IF EXISTS (
    SELECT *
    FROM sys.objects
    WHERE object_id = OBJECT_ID(N''[dbo].[<functionName>]'')
    AND type IN ( N''FN'', N''IF'', N''TF'', N''FS'', N''FT'' )
)
BEGIN
    DROP FUNCTION [dbo].[<functionName>]
END
'

-- SQL templates (Create function)
DECLARE @FunctionTemplate NVARCHAR(MAX) = '
-- Function to identify all tables having an FK reference to a specific parant table and key value
-- Generated: <GeneratedDateTime>
CREATE FUNCTION [<functionName>](
    @SchemaName sysname,
    @TableName sysname,
    @RowId <PrimaryKeyDataType><CharKeyTypeSuffix>
)
RETURNS @ReferencingTables TABLE (SchemaName sysname, TableName sysname)  
AS      
BEGIN    
    IF 1 = 0
    BEGIN
        DECLARE @Noop INT  -- Syntactic placeholder
    END
<ParentTablesSql>

    RETURN
END
'

-- SQL templates (Code to search for FK references to one particlar parent table)
DECLARE @ParentTableTemplate NVARCHAR(MAX) = '
    ELSE IF @TableName = ''<ParentTableName>'' AND @SchemaName = ''<ParentSchemaName>''
    BEGIN
        INSERT @ReferencingTables
        SELECT SchemaName = '''', TableName = '''' WHERE 1 = 0  -- Syntactic placeholder
<ChildTablesSql>
    END'

-- SQL templates (Code to search for FK references from one particlar child table and FK)
DECLARE @ChildTableTemplate NVARCHAR(MAX) = '
        UNION ALL SELECT SchemaName = ''<ChildSchemaName>'', TableName = ''<ChildTableName>'' WHERE EXISTS(SELECT * FROM [<ChildSchemaName>].[<ChildTableName>] T WHERE T.[<ChildColumnName>] = @RowId)'

-- Pull it all together, substituting schema, table, and column names as we go 
-- and concatenating intermediate results using the XML PATH('') trick.
DECLARE @GeneratedSql NVARCHAR(MAX) = (
    SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        @FunctionTemplate
        , '<GeneratedDateTime>', @GeneratedDateTime)
        , '<functionName>', @FunctionName)
        , '<PrimaryKeyDataType>', @PrimaryKeyDataType)
        , '<CharKeyTypeSuffix>', @CharKeyTypeSuffix)
        , '<ParentTablesSql>', ISNULL(pts.ParentTablesSql, @NoTablesFoundMessage))

    FROM (
        SELECT ParentTablesSql = REPLACE((
            SELECT REPLACE(REPLACE(REPLACE(
                @ParentTableTemplate
                , '<ParentSchemaName>', ParentSchemaName)
                , '<ParentTableName>', ParentTableName)
                , '<ChildTablesSql>', cts.ChildTablesSql)
            FROM (
                SELECT DISTINCT fk.ParentSchemaName, fk.ParentTableName
                FROM @FkInfo fk
            ) p
            CROSS APPLY (
                SELECT ChildTablesSql = REPLACE((
                    SELECT REPLACE(REPLACE(REPLACE(
                        @ChildTableTemplate
                        , '<ChildSchemaName>', fk.ChildSchemaName)
                        , '<ChildTableName>', fk.ChildTableName)
                        , '<ChildColumnName>', fk.ChildColumnName)
                    FROM @FkInfo fk
                    WHERE fk.ParentSchemaName = p.ParentSchemaName AND fk.ParentTableName = p.ParentTableName
                    ORDER BY fk.ChildSchemaName, fk.ChildTableName, fk.ChildColumnName
                    FOR XML PATH('')
                ), '&#x0D;', CHAR(13))
            ) cts
            ORDER BY p.ParentSchemaName, p.ParentTableName
            FOR XML PATH('')
        ), '&#x0D;', CHAR(13))
    ) pts
)

-- Generate drop sql also
DECLARE @DropFunctionSql NVARCHAR(MAX) = REPLACE(@DropFunctionTemplate, '<functionName>', @FunctionName)

-- Combine for final output
DECLARE @AllSql NVARCHAR(MAX) =
    @DropFunctionSql + 'GO' + CHAR(13) + CHAR(10)
    + @GeneratedSql + 'GO' + CHAR(13) + CHAR(10)

--PRINT @AllSql
SELECT CAST('<root><![CDATA[' + @AllSql + ']]></root>' AS XML) -- If the above is too long to "print".

-- Optional direct execute (must run as two separate executions).
--EXEC sp_executesql @DropFunctionSql
--EXEC sp_executesql @GeneratedSql

如果这不合适,您可能必须在存储过程中编写一些代码,该存储过程可以根据当前架构即时生成和执行动态 sql。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多