【问题标题】:SQL: select different columns to be returned from a stored procedureSQL:选择要从存储过程中返回的不同列
【发布时间】:2018-06-22 10:01:36
【问题描述】:

假设我有一个表mytable 有几列(这并不完全正确,请参阅编辑):

mytable
id    data        description          created_at      viewed_times
1      10        'help wanted'      '20180101 04:23'       45
2      20    'customer registered'  '20180504 03:12'        1
...

我创建了一个从该表返回数据的存储过程。问题是有时我需要它只返回iddata 列,有时我需要其他信息,例如descriptioncreated_at 等。

我的想法是创建三个虚拟变量@display_description@display_created_date@display_viewed_times。当虚拟变量等于 1 时,我显示相应的列。例如,对于命令

exec my_procedure @display_description = 1

我期待输出

id    data        description        
1      10        'help wanted'      
2      20    'customer registered'  
...

我在程序中的实现方式是:

if @display_description = 1
    select id, data, description from mytable
else 
    select id, data from mytable

问题是,如果我想要 3 个开关(每列一个),我必须在我的 if 语句中编写 8 个条件。 (我的select语句比较复杂。另外,像viewed_times这样的一些列,如果我想显示的话,必须计算出来。因此,写很多if语句会使查询很笨拙,我想避免这种情况)

有没有一种简单的方法可以根据开关选择列?如果不是,您会推荐什么方法从存储过程中返回不同数量的列?

编辑:对不起,我很遗憾表 mytable 已经存在所有列。事实并非如此,某些列必须在显示之前进行计算。例如,列 viewed_times 不存在。要显示它,我需要执行以下操作:

if @display_description = 1
    ~ several selects to calculate the column viewed_times ~
    select id, data, description from mytable join table in which I just calculated the viewed_times column
else 
    select id, data from mytable

计算这些列非常耗时,我只想在需要显示这些列时才这样做。

这就是动态 SQL 可能不起作用的原因

更新:我接受了动态 SQL 的答案,但我所做的是:

if @display_viewed_times = 1
begin   
    ~ calculate column viewed_times ~
    update my_table 
    ~ add column viewed_times to the table my_table ~
end

对于每个可选列。然后我使用返回表格

select * from my_table

这给了我不同数量的列,具体取决于开关

【问题讨论】:

  • 可以使用动态查询。也谷歌厨房水槽
  • 为什么不直接使用select . . . from?存储过程能给你带来什么价值?
  • 根据传递的参数返回不同结果集形状的存储过程是有缺陷的界面设计,恕我直言。
  • 我必须同意@DanGuzman。将每个排列写成一个单独的过程,然后让调用应用程序决定调用哪一个。
  • @EricBrandt 在这种情况下,我将不得不使用基本相同的代码编写 8 个不同的程序(包括和不包括每一列)。还是我误会了?

标签: sql sql-server stored-procedures dynamic-columns


【解决方案1】:

一种方法是使用动态 SQL。这是伪 SQL,因为这里没有信息来获得完整的答案。它也未经测试,因为我没有太多/任何数据可以真正运行它。

--Your input parameters
DECLARE @description bit, @createdate bit, @viewedtimes bit /*...etc...*/;

--Now the SP
DECLARE @SQL nvarchar(MAX);

SET @SQL = N'SELECT ' +
           STUFF(CONCAT(N','+ NCHAR(10) + N'       ' + CASE WHEN @description = 1 THEN QUOTENAME(N'description') END,
                        N','+ NCHAR(10) + N'       ' + CASE WHEN @createdate = 1 THEN QUOTENAME(N'created_at') END,
                        N','+ NCHAR(10) + N'       ' + CASE WHEN @viewedtimes = 1 THEN QUOTENAME(N'viewed_times') END),1,9,N'') + NCHAR(10) +
           N'FROM YourTable' + NCHAR(10) + 
           N'WHERE...;';
PRINT @SQL; --your best debugging friend.
EXEC sp_executesql @SQL /*N'@someParam int', @someParam = @inParam*/;

确保在使用 Dyanmic SQL 时正确地为查询设置参数。 不要连接你的字符串!!!

例如,正确的格式是N'WHERE yourColumn = @yourParam AND OtherColumn = @otherParam',然后在sp_executesql 中提供@yourParam@otherParam 的值(正如我在评论中演示的那样)。

但是,不要这样做: N'WHERE yourColumn = ''' + @myParam + N''' AND otherColumn = ' + CONVERT(varchar(5),@secondParam)。这将对 SQL 注入开放(这不是你的朋友)。

【讨论】:

  • 您的答案在我描述的环境中肯定有效,我相信它会对遇到同样问题的人有所帮助。我忘了提,表mytable 的某些列必须计算(将mytable 视为过程的结果),并且计算可能很繁重,所以我希望它仅在我愿意的情况下计算显示它。因此,@SQL 中的代码将包含多个 SELECT 语句,我想避免这些语句。
  • @AlexandrKapshuk 然后而不是简单地让QUOTENAME(N'description') 包含您的表达式。例如CASE WHEN @description = 1 THEN N'SUM(CASE [description] WHEN ''Cheese'' THEN [Price] END) AS [description]' END。您仍然可以在动态 SQL 中包含表达式。
  • 如果找不到其他解决方案,我可能会这样做。我想避免这种情况的原因是因为代码在转换为字符串时变得不可读。特别感谢有关注射的提示!我会记住这一点
  • @AlexandrKapshuk 这就是你最好的朋友的目的。
【解决方案2】:

我会将存储过程更改为表值函数并修改外部“select from”。

例如:

if($value1 === 1)
  $sql = "SELECT c1, c2 FROM dbo.my_udf";
elseif($value1 ===2)
  $sql = "SELECT c1, c3, c4 FROM dbo.my_udf";

【讨论】:

  • 那不是 T-SQL。看来您已经假设 OP 正在使用的应用程序语言(是 PHP 吗?),但是,他们没有提到这一点。充其量,这是一个非常疯狂的猜测。对于同一件事,拥有多个功能也不是一个很好的可扩展解决方案。如果查询需要更改,您需要更改 11 个函数,而不是 1 个。
  • 当然这不是T-SQL。我的建议是将 SP 修改为函数。该代码仅显示如何从函数中选择不同的列。由于没有SP的代码,我无法发布所需功能的代码。
猜你喜欢
  • 2011-02-20
  • 1970-01-01
  • 2014-06-07
  • 2015-01-24
  • 1970-01-01
  • 2021-10-06
  • 2022-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多