【问题标题】:How can I use a dynamic table name in the FROM area of a SQL query?如何在 SQL 查询的 FROM 区域中使用动态表名?
【发布时间】:2011-12-30 20:33:51
【问题描述】:

原问题: 我正在使用一个每月生成一个新表并将YYYY_MM 附加到新表名的数据库。我需要编写一个查询来查看当前月份的表。除了日期函数,我发现我无法建立表名!

示例表名称为DOCUMENTS_2011_10

我尝试使用这样的基本脚本,但在语法问题上失败了:

SELECT * FROM ('DOCUMENTS_'+'2011_10')

有什么建议吗?

附加信息: 这些表是由我们的企业门户应用程序(Oracle WebCenter Interaction)生成的。我无法控制重新设计此应用程序以更改其处理数据的方式。

我被要求从每月生成的这些表中提取显示关键数据的报告。

我的方法是创建一个 SQL 报告服务订阅,每月以 Excel 格式通过电子邮件发送数据。
我需要有关此查询的帮助才能将其插入报告服务,这样我就不必每个月都涉足查询来更改表名。

由于报告服务将每月触发并生成报告,因此不需要数据存档或任何类型的临时功能。

使用动态 SQL,我能够设置表名,但不幸的是,当我尝试添加查询的其余部分时,它会抱怨字符限制问题。知道如何解决这个问题吗?

这让我找到了正确的表格:

Declare @tblName Varchar(400)
Declare @SQL Varchar(500)

Set @tblName = 
'analyticsdbuser.ASFACT_DOCUMENTVIEWS_'
+ CONVERT(VARCHAR,DATEPART(yyyy,GETDATE())) 
+ '_'
+ CONVERT(VARCHAR, RIGHT('0' + RTRIM(MONTH(GETDATE())-1), 2));

SET @SQL = 'SELECT * FROM ' + @Tblname;
Exec(@SQL)

这是另一个需要执行但不适合 @SQL 的查询:

SELECT   t2.ID,
         t2.USERID,
         t3.NAME,
         t2.DOCUMENTID,
         t1.NAME AS DOC_NAME,
         t4.PROPERTYID,
         t5.NAME AS PROP_NAME,
         t4.VALUE

FROM     ASFACT_DOCUMENTVIEWS_2011_10 AS t2 INNER JOIN
         ASDIM_USERS AS t3 ON t3.USERID = t2.USERID INNER JOIN
         ASDIM_USERPROPERTYVALUES AS t4 ON t4.USERID = t2.USERID INNER JOIN
         ASDIM_KDDOCUMENTS AS t1 ON t1.ID = t2.DOCUMENTID INNER JOIN
         ASDIM_USERPROPERTIES AS t5 ON t4.PROPERTYID = t5.PROPERTYID

WHERE    (t2.DOCUMENTID IN ('33449', '36241', '36566')) AND
         (t4.PROPERTYID IN (26, 156, 157, 158, 159, 325, 160, 162))

ORDER BY t2.DOCUMENTID,
         t3.NAME;

分区问题 我不熟悉一些人提到的分区概念,现在会研究它。

如果重要的话,这些月度表中的每一个只有大约 20k 行和 1.5mb(我们不是一家大公司,这个软件是为在每个月度表中获得数百万行的公司设计的)

【问题讨论】:

  • 您是否考虑过使用 SQL Server 的分区功能来水平拆分表?如果您完全可以避免动态命名的表,那么动态 SQL 将为您省去很多麻烦。
  • 是的,如果可用的话,应该使用分区。否则......不会完全消除对动态 SQL 的需求,但我们在这里所做的一件事是为所需表创建一个临时 alias(动态),然后使用(常量) SQL 中别名的名称。如果您将其设为视图,这也会产生以下效果:能够聚合来自多个来源(例如三个月)的数据。
  • 如果动态 SQL 不适合变量,并且您没有使用分区,您可以缩短变量的内容。只需抓住一张桌子并将结果放入临时表中。然后再次使用临时表而不是动态命名的表进行主查询,更长的查询不需要动态 SQL。

标签: sql sql-server-2005 sql-server-2008 partitioning dynamic-sql


【解决方案1】:

对象和列名在 SQL Server 中不能是动态的。

您必须使用动态 SQL 来构建它,例如使用 sp_executesql

...
SET @SQL = 'DOCUMENTS_'+'2011_10';
EXEC sp_executesql @sql ...

也就是说,在对象名称中嵌入这样的信息通常是不好的做法。如果您想在一次查询中获取 3 个月的数据会怎样?

【讨论】:

  • 另外,要小心,因为如果您在存储过程中执行此操作,它将以 PROC 所有者的权限而不是当前用户的权限执行。
  • @JohnFx:是的,我不喜欢动态 SQL 的原因之一
  • ...并且有很多理由可以避免它。因此,OP 有充分理由重新考虑像这样动态命名表。
  • 感谢您提供的有用信息。这是一个大型企业应用程序系统的一部分,旨在每月处理数百万行数据,并对数据进行复杂的报告(不是我做的方式)。出于我的目的,历史数据不在范围内。我严格需要返回过去几个月的信息。
【解决方案2】:

不知道更多 - 有很多选择

  1. 写动态sql:http://www.tek-tips.com/faqs.cfm?fid=3132

    Set @SQL = 'SELECT * From #TempTable Where EmpID in (' + @INList +')'
    Exec(@SQL)
    

    可能是您正在寻找的,但是

  2. 您是否考虑过分区视图,并且只能从视图访问?这样做的好处是将事物保存在各自的表中,但是 - 提供对数据的访问,就好像 ti 在一个地方一样。

【讨论】:

  • 您的第一个构造与动态表名无关,存在 SQL 注入风险
  • 感谢您的分区建议 - 我会调查这一点,因为我采用的方法似乎很笨重。
  • 毫无疑问这是一种注入风险,但它确实回答了用户提出的确切问题。问题不是“有没有安全的方法”,而是我如何完成这项确切的任务。这就是为什么我给出了两个答案...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-17
  • 2014-01-07
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 2021-12-02
相关资源
最近更新 更多