我在SSMS-2017中开发了这样的SQL查询:
DECLARE @property NVARCHAR(MAX) = @p;
SET @property = REPLACE(@property, '''', '');
DECLARE @propList TABLE (hproperty NUMERIC(18, 0));
IF CHARINDEX('SELECT', @property) > 0 OR CHARINDEX('select', @property) > 0
BEGIN
INSERT INTO @propList
EXECUTE sp_executesql @property;
END;
ELSE
BEGIN
DECLARE @x TABLE (val NUMERIC(18, 0));
INSERT INTO @x
SELECT CONVERT(NUMERIC(18, 0), strval)
FROM dbo.StringSplit(@property, ',');
INSERT INTO @propList
SELECT val
FROM @x;
END;
SELECT ...columns...
FROM ...tables and joins...
WHERE ...filters...
AND HMY IN (SELECT hproperty FROM @propList)
问题是,参数@p的值可能是ID列表(例如:1、2、3、4)或直接选择查询(例如:从mytable中选择ID,其中code = 'A123')。
如上所述,代码运行良好。但是,这会在我们的系统中引起问题(因为我们使用Yardi7-Voyager),我们只需要保留select语句作为查询。为了管理它,我打算创建一个函数,并在where子句中使用它,例如:
WHERE HMY IN (SELECT myFunction(@p))
但是,我无法对其进行管理,因为我无法在SQL函数中执行动态查询。然后我被堆积了。任何解决此问题的想法都将受到赞赏。
答案 0 :(得分:1)
其他人指出,对此的最佳解决方案是设计更改,我同意他们的观点。但是,我也想将您的问题视为学术问题,并在将来不可能/不希望进行设计更改的用例中回答任何将来的问题时予以回答。
我可以想到两种方法,只要您没有提到的其他任何限制,您就可以一次选择地完成自己想做的事情。为了简短起见,我将为您提供可以适应您以及未来读者情况的伪代码:
您可以将上面的代码合并到存储过程中而不是函数中,因为与函数不同,存储过程确实允许动态sql。然后,您应用中的SELECT查询将为SELECT from OPENQUERY(Execute Your Stored Prodedure)。
我大约有99%的人肯定没有人会愿意使用它,但是我要说的是,它在学术上与我所知道的一样完善。
第二种可能性仅在应用程序可能支持的已知查询数量有限且已知的情况下才有效。例如,您只能从Properties
中过滤TableA
或column1
中过滤TableB
和/或{{1 }}。
可能不仅仅是这些可能性,但必须是有限的已知数量,并且可能性越大,代码将变得越复杂和冗长。
但是,如果是这种情况,您可以简单地从所有可能的方案的UNION ALL中进行选择,并进行选择,以便UNION ALL中只有一个SELECT会返回结果。
例如:
Column2
请注意,Column3
不是内置函数。您必须编写它。它将解析SELECT ... FROM TableA WHERE Column1=fnGetValue(@p, 'Column1')
AND CHARINDEX('SELECT', @property) > 0
AND CHARINDEX('TableA', @property) > 0
AND CHARINDEX('Column1', @property) > 0
AND (Whatever other filters are needed to uniquely identify this case)
UNION ALL
SELECT
...
中的字符串,找到“ Column1 =”的位置,并返回其后的所有值。
在UNION ALL的末尾,您需要向查询中添加最后一个UNION ALL,以处理用户通过逗号分隔的字符串而不是查询的情况,但这很容易,因为所有无需在代码中执行填充表变量的步骤。您可以像这样简单地进行最终查询:
fnGetValue()
我敢肯定,这种可能性比它的价值要大得多,但这只是一个示例,通常可以用常规SQL替换动态SQL,而普通SQL仅涵盖您希望将动态sql用作所有可能的选项能够应付。