SQL存储过程中的动态排序(Dynamic Sorting within SQL Stored Pr

2019-07-20 02:26发布

这是我花了几个小时,在过去研究的问题。 在我看来,是一件应该已经被现代解决RDBMS解决方案,但至今我还没有发现任何真正解决了我所看到的是在任何Web或Windows应用程序与数据库后端一个令人难以置信的共同需要。

我动态排序发言。 在我的幻想世界,它应该是简单,就像这样:

ORDER BY @sortCol1, @sortCol2

这是新手SQL给出了典型的例子存储过程遍布在互联网论坛上的开发者。 “为什么不是这可能吗?” 他们问。 不变的是,有人最终走来讲授他们关于一般的存储过程的执行计划,以及其他各种原因编译性质为什么它是不可能直接把参数为ORDER BY子句。


我知道你们当中有些人已经在考虑:“让客户来排序,然后” 当然,这种卸载从您的数据库的工作。 在我们的例子不过,我们的数据库服务器甚至没有打破的时候出了一身汗99%,他们甚至不多核又或任何的系统架构,发生每6个月的其他无数的改进。 仅仅出于这个原因,有我们的数据库手柄排序不会是一个问题。 此外,数据库是在整理非常好。 他们正在为它做了优化,并有多年得到它的权利,做它的语言是非常灵活,直观和简单,首先任何初学者的SQL作家知道如何做到这一点,更重要的是,他们知道如何编辑它,做出改变,做保养,等等。当你的数据库被征税远,你只是想简化(缩短!)的开发时间,这似乎是一个显而易见的选择。

再有就是网络问题。 我打得周围的JavaScript,会做HTML表格的客户端排序,但他们不可避免地不是我的需要具有足够的灵活性,并再次,因为我的数据库并不过分征税,可以做到真正轻松排序真的,我也很难证明则需重新编写或滚我,自己的JavaScript分拣机的时间。 同样的一般无二,服务器端排序,尽管它已经可能多了JavaScript的首选。 我不是一个特别喜欢的DataSet的开销,因此起诉我。

但这带回,这是不可能的点 - 或者更确切地说,不容易。 我所做的,与之前的系统,让动态排序的一个令人难以置信的黑客攻击方式。 这是不是漂亮,也不是直观的,简单的,或柔性和初学者的SQL作家将秒钟内消失。 这已经是希望成为与其说是“解决方案”,而是一个“复杂”。


下面的例子并不意味着揭露任何形式的最佳做法或良好的编码风格或任何东西,也不是我的指示能力的T-SQL程序员。 他们是他们是什么,我完全承认他们是混乱的,坏的形式,只是普通的黑客。

我们传递一个整数值作为参数传递给存储过程(我们称之为参数只是“排序”),并从我们确定​​了一堆其他变量。 例如...假设排序为1(或默认):

DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)

SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';

IF @sort = 1                -- Default sort.
BEGIN
    SET @sortCol1 = @col1;
    SET @dir1 = 'asc';
    SET @sortCol2 = @col2;
    SET @dir2 = 'asc';
END
ELSE IF @sort = 2           -- Reversed order default sort.
BEGIN
    SET @sortCol1 = @col1;
    SET @dir1 = 'desc';
    SET @sortCol2 = @col2;
    SET @dir2 = 'desc';
END

你已经可以看到,如果我宣布更多@colX变量来定义其他列我真的可以发挥创意与列基础上的“排序”值进行排序...使用它,它通常最终看起来像以下令人难以置信的凌乱的条款:

ORDER BY
    CASE @dir1
        WHEN 'desc' THEN
            CASE @sortCol1
                WHEN @col1 THEN [storagedatetime]
                WHEN @col2 THEN [vehicleid]
            END
    END DESC,
    CASE @dir1
        WHEN 'asc' THEN
            CASE @sortCol1
                WHEN @col1 THEN [storagedatetime]
                WHEN @col2 THEN [vehicleid]
            END
    END,
    CASE @dir2
        WHEN 'desc' THEN
            CASE @sortCol2
                WHEN @col1 THEN [storagedatetime]
                WHEN @col2 THEN [vehicleid]
            END
    END DESC,
    CASE @dir2
        WHEN 'asc' THEN
            CASE @sortCol2
                WHEN @col1 THEN [storagedatetime]
                WHEN @col2 THEN [vehicleid]
            END
    END

显然,这是一个非常精简的例子。 真正的东西,因为我们通常有四分,五列支持排序上,每一个可能的二次甚至第三列于除了排序(例如日期降序然后通过名称升序排序的次要),每个支持双定向排序这有效地加倍病例数。 是啊...它变得毛茸茸的真快。

我们的想法是,人们可以“轻松地”改变那种情况下,使得vehicleid获取storagedatetime前整理...但伪灵活性,至少在这个简单的例子,真的仅止于此。 从本质上讲,一个失败的测试(因为我们的排序方法不围绕适用于这一次),每种情况下呈现NULL值。 因而你结束了,其功能类似下面的条款:

ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah

你的想法。 它的工作原理,因为SQL Server有效地忽略的条款,以便空值。 这是令人难以置信的难以维持,因为任何人与任何SQL基本操作知识很可能会看到。 如果我已经失去了任何的你,不心疼。 我们花了很长时间才得到它的工作,我们仍然感到困惑,试图对其进行编辑或创建新的喜欢它。 值得庆幸的是它不需要经常更换,否则会很快成为“不必麻烦了。”

然而,它工作。


我的问题则是: 有没有更好的办法?

我没事比存储过程之外的其他解决方案,因为我知道这可能只是没有要走的路。 最好,我想知道如果任何人都可以在存储过程中做的更好,但如果没有,你怎么处理都让用户动态排序与ASP.NET数据表(双向,太)?

而感谢您阅读(或至少略读)这么长的问题!

PS:要庆幸我没有表现出我的,支持动态排序,列的动态过滤/文本搜索,分页通过ROWNUMBER()在一个存储过程的例子, 尝试......与错误交易rollbacking赶上... “巨兽大小”甚至没有开始来形容他们。


更新:

  • 我想避免动态SQL。 一起解析字符串并运行在其上的EXEC击败了很多具有在首位的存储过程的目的。 有时候我在想但如果做这样的事情的利弊不会是值得的,至少在这些特殊的动态排序情况。 不过,我总觉得脏,每当我做动态SQL字符串这样的 - 像我仍然生活在传统的ASP世界。
  • 很多我们想在第一时间存储过程的原因是为了安全 。 我不明白,使对安全问题的号召,只建议的解决方案。 在SQL Server 2005,我们可以设置权限(在必要时每个用户的基础上)在个人存储过程的模式级别,然后拒绝对直接在表的任何查询。 批评这种做法的利弊,也许是另一个问题,但同样这不是我的决定。 我只是前导码猴。 :)

Answer 1:

是啊,这是一个痛苦,和你做它的方式类似于我做什么:

order by
case when @SortExpr = 'CustomerName' and @SortDir = 'ASC' 
    then CustomerName end asc, 
case when @SortExpr = 'CustomerName' and @SortDir = 'DESC' 
    then CustomerName end desc,
...

这对我来说,仍然比从构建动态代码SQL,它变成了可扩展性和维护的噩梦DBA们要好得多。

我从代码做的是重构分页和排序,所以我至少没有大量的重复与有用于填充值@SortExpr@SortDir

至于SQL而言,保持设计和格式化不同存储过程之间是相同的,所以当你去做出改变它至少整洁和辨认。



Answer 2:

这种方法可以使从顺序被复制两次排序的列,并且是一个小更可读IMO:

SELECT
  s.*
FROM
  (SELECT
    CASE @SortCol1
      WHEN 'Foo' THEN t.Foo
      WHEN 'Bar' THEN t.Bar
      ELSE null
    END as SortCol1,
    CASE @SortCol2
      WHEN 'Foo' THEN t.Foo
      WHEN 'Bar' THEN t.Bar
      ELSE null
    END as SortCol2,
    t.*
  FROM
    MyTable t) as s
ORDER BY
  CASE WHEN @dir1 = 'ASC'  THEN SortCol1 END ASC,
  CASE WHEN @dir1 = 'DESC' THEN SortCol1 END DESC,
  CASE WHEN @dir2 = 'ASC'  THEN SortCol2 END ASC,
  CASE WHEN @dir2 = 'DESC' THEN SortCol2 END DESC


Answer 3:

动态SQL仍是一个选项。 你只需要决定选择是否比你现在有更可口。

下面是显示的文章: http://www.4guysfromrolla.com/webtech/010704-1.shtml 。



Answer 4:

我的应用做了很多,但他们都是动态生成SQL。 然而,当我处理存储过程,我这样做:

  1. 使存储过程返回的值的表的功能 - 无排序。
  2. 然后在你的应用程序代码做一个select * from dbo.fn_myData() where ... order by ...这样你就可以动态地指定排序顺序出现。

那么至少有活力的部分是在你的应用程序,但该数据库还在做着繁重。



Answer 5:

有一对夫妇,你可以在这个破解方式不同。

先决条件:

  1. 只有一个在SP SELECT语句
  2. 遗漏任何排序(或有一个默认)

然后插入到一个临时表:

create table #temp ( your columns )

insert #temp
exec foobar

select * from #temp order by whatever

方法2:设置链接服务器回自己,然后从该使用OPENQUERY选择: http://www.sommarskog.se/share_data.html#OPENQUERY



Answer 6:

可能有第三个选择,因为你的服务器有大量的空闲周期的 - 使用一个辅助程序通过一个临时表来进行排序。 就像是

create procedure uspCallAndSort
(
    @sql varchar(2048),        --exec dbo.uspSomeProcedure arg1,'arg2',etc.
    @sortClause varchar(512)    --comma-delimited field list
)
AS
insert into #tmp EXEC(@sql)
declare @msql varchar(3000)
set @msql = 'select * from #tmp order by ' + @sortClause
EXEC(@msql)
drop table #tmp
GO

警告:我没有测试过这一点,但它“应该”在SQL Server 2005的工作(这将创建一个从没有事先指定列的结果集的临时表。)



Answer 7:

存储过程技术(黑客?)我用,以避免某些工作动态SQL是有一个独特的排序列。 也就是说,

SELECT
   name_last,
   name_first,
   CASE @sortCol WHEN 'name_last' THEN [name_last] ELSE 0 END as mySort
FROM
   table
ORDER BY 
    mySort

这一个是容易击败就范 - 你可以在你mySort列Concat的领域,用数学或日期函数等颠倒顺序

最好不过,我用我的asp.net GridView的或其他物体与内置的排序来进行排序,我AFTER检索数据来回的SQL服务器。 或者,即使它没有内置 - 例如,数据表等,在asp.net。



Answer 8:

在某些时候,不也成为值得存储过程移开,只需使用参数化查询,以避免这类两轮牛车的?



Answer 9:

我同意,使用客户端。 但现在看来,是不是你想要听到的答案。

因此,它是完美的事情是这样的。 我不知道你为什么会想改变它,甚至问:“有没有更好的办法。” 真的,它应该被称为“路”。 此外,它似乎工作和适应项目蛮好的需求,可能会是足够的可扩展几年来。 由于你的数据库不征税和排序是真的很容易 ,应该保持这种方式在今后几年。

我不会出汗。



Answer 10:

当你分页排序的结果,动态SQL是一个不错的选择。 如果你是偏执SQL注入,你可以用它代替列名的列数。 我使用负值为下降前已经这样做了。 事情是这样的......

declare @o int;
set @o = -1;

declare @sql nvarchar(2000);
set @sql = N'select * from table order by ' + 
    cast(abs(@o) as varchar) + case when @o < 0 then ' desc' else ' asc' end + ';'

exec sp_executesql @sql

然后你只需要确保的数量是内1〜#列。 你甚至可以扩大这列数的列表,并解析成使用类似的功能INTS的表此 。 然后,你会by子句,像这样建的顺序...

declare @cols varchar(100);
set @cols = '1 -2 3 6';

declare @order_by varchar(200)

select @order_by = isnull(@order_by + ', ', '') + 
        cast(abs(number) as varchar) + 
        case when number < 0 then ' desc' else '' end
from dbo.iter_intlist_to_tbl(@cols) order by listpos

print @order_by

一个缺点是你必须记住在客户端的每一列的顺序。 尤其是,当你不显示所有的列,或者你以不同的顺序显示出来。 当客户想要进行排序,你的列名映射到列顺序和产生整数列表。



Answer 11:

针对执行排序在客户端的参数是大容量数据和分页。 一旦你的行数获得超越你可以很容易地显示你经常排序为跳过的部分/带,这你可能想在SQL运行。

对于实体框架,你可以使用一个存储过程来处理您的文本搜索。 如果你遇到同样的问题,我见过的解决方案是使用一个存储过程的搜索,只返回一个ID键为比赛设定。 接下来,使用IDS在列表中(含)重新查询(与排序)对分贝。 EF处理这种相当好,即使在ID集是相当大的。 是的,这是两个来回,但它可以让你始终保持在DB,这在某些情况下,重要的你的排序,并防止您在存储过程编写逻辑的一大堆。



Answer 12:

如何处理上显示的结果排序的东西 - 网格,报告等,而不是在SQL?

编辑:

为了澄清的事情,因为这个答案得到了同期有所下降,投票,我会详细一点?

你说你知道客户端排序,但要避开它。 这是你的电话,当然。

我想指出的是,虽然是做它的客户端,你可以一次提取数据,然后使用它不过你想要的 - 与每次都做多次往返来回服务器排序得到改变。

您的SQL Server是没有得到征税,现在和这真棒。 它不应该是。 但是,仅仅因为它是没有超载,并不意味着它会留下这样的永远。

如果你使用任何新的ASP.NET的东西显示在网络上,有很多的东西就在已经出炉。

是否值得加入这么多的代码,每个存储过程只是为了处理排序? 同样,您的电话。

我不是一个谁最终将负责支持它。 但是想一想,作为列添加什么将参与/由存储过程(无需修改CASE语句)中使用的各种数据集内删除或当突然而不是两列排序,用户决定他们需要三 - 需要您现在更新使用此方法的存储过程的每一个。

对我来说,这是值得的,以获得工作的客户端解决方案,并将其应用到数据的面向用户的显示器的极少数,并用它来完成。 如果添加了新列,它已经处理。 如果用户想通过多列排序,它们可以通过两个或其中有20排序。



Answer 13:

对不起,我迟到了,但这里对于那些谁真的想避免动态SQL的另一种选择,但希望它提供了灵活性:

相反,动态生成在飞行的SQL,编写代码来生成每一个可能变化的独特PROC。 然后,你可以写代码来看看搜索选项的方法,并将它选择合适的PROC打电话。

如果你只有一些变化那么你可以手动创建特效。 但是,如果你有很多的变化,然后,而不必保持所有这些,你只是保持你的PROC发电机,而不是把它重新创建。

作为一个额外的好处,你会得到更好的性能更好的SQL计划做这种方式太。



Answer 14:

该解决方案可能仅在.NET中工作,我不知道。

我把数据放到了C#与by子句中的SQL命令的初始排序顺序,把在一个数据视图数据,它缓存在一个会话变量,并用它来构建一个页面。

当用户单击列标题排序(或网页,或过滤器),我不回去的数据库。 相反,我回到我的缓存数据视图,并设置它的“排序”属性来我动态构建表达式,就像我会动态SQL。 (我做过滤以同样的方式,使用“的RowFilter”属性)。

你可以看到/感觉到它在我的应用程序,BugTracker.NET的演示工作在http://ifdefined.com/btnet/bugs.aspx



Answer 15:

您应该避免在SQL Server的排序,除非在必要时。 为什么不排序的应用程序服务器或客户端上? 此外.NET泛型不例外sortin



文章来源: Dynamic Sorting within SQL Stored Procedures