***高分请问关于用户自定义函数和动态SQL的问题***

BrentIvan 2010-11-04 05:51:15
有一个表(T)结构如下:
Id Name Condition
1 'A1' '@P < 1'
2 'A2' '@P < 4 AND @P >1'
3 'A3' '@P >=3 OR @P < 2'
4 'A4' '@P = 5'

看数据能理解,Condition里存的是一个表达式,而且里面是有参数的(@P)
我现在需要写一个自定义函数,能带参数查出表达式结果为True的数据:
SELECT * FROM T WHERE fn_Check(T.Condition, 2) = 1

其中,2是参数,会替Condition中的@P,应该返回数据为Id= 2, 3的行

不知道这个fn_Check怎么写,请高手不吝赐教!!!

难点是:
1、用户自定义函数中不支持动态SQL
2、不希望写成存储过程,因为存储过程肯定会弄成游标一行行滚,这样效率太低(我的数据行很多)
...全文
193 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaoxinghappy 2011-01-22
  • 打赏
  • 举报
回复
create function fn_Check(@con varchar(100), @par int)
returns bit
as
begin
declare @a bit
set @con = 'if ' +replace(@con, '@P', ltrim(@par))+' set @a=1 else @a=0'
exec sp_executesql @con ,N'@a bit output',@a bit
return @a
end;
go

declare @t table (id int identity, name varchar(10), condiction varchar(100));
insert into @t (name,condiction)
select 'A1','@P < 1' union all
select 'A2','@P < 4 AND @P >1' union all
select 'A3','@P >=3 OR @P < 2' union all
select 'A4','@P = 5';

select * from @t where dbo.fn_Check(condiction,2)=1;
BrentIvan 2010-11-10
  • 打赏
  • 举报
回复
非常感谢各位高手,xman_78tom的结果是正确的,而且思路完全符合我的要求。多谢多谢!
Vidor也提出了解决方案,但这个方案不完美,因为Condition里的东西无法枚举,所以这个方案无法完美解决问题。但还是多谢你的帮助。

二位接分吧!
xman_78tom 2010-11-04
  • 打赏
  • 举报
回复
CRL 函数可以实现,不过要 SQL Server 2005 才可以支持。

.net 程序集 chkexp.dll

using System;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;

public class CheckExpression
{
[SqlFunction(IsDeterministic=true, IsPrecise=true,DataAccess=DataAccessKind.Read)]
public static int ChkExp (String exp, int param)
{
String expr = "select (case when " + exp + " then 1 else 0 end)";
using (SqlConnection conn = new SqlConnection("context connection = true"))
{
conn.Open();
SqlCommand cmd = new SqlCommand(expr, conn);
cmd.Parameters.AddWithValue("@p", param);
return (int) cmd.ExecuteScalar();
}
}
}


CLR 函数 ufn_chkexp

use master
go

exec sp_configure 'show advanced options',1;
go
reconfigure
go
exec sp_configure 'clr enabled',1;
go
reconfigure
go

if OBJECT_ID('dbo.ufn_chkexp') is not null
drop function dbo.ufn_chkexp;
go

if exists(select * from sys.assemblies where name='CheckExpression')
drop assembly CheckExpression;
go
create assembly CheckExpression authorization dbo
from 'c:\chkexp.dll' with permission_set=safe;
go

create function dbo.ufn_chkexp (@exp nvarchar(4000), @param int)
returns int as external name CheckExpression.CheckExpression.ChkExp;
go


测试

declare @t table (id int identity, name varchar(10), condiction varchar(100));
insert into @t (name,condiction)
select 'A1','@P < 1' union all
select 'A2','@P < 4 AND @P >1' union all
select 'A3','@P >=3 OR @P < 2' union all
select 'A4','@P = 5';

select * from @t where master.dbo.ufn_chkexp(condiction,2)=1;
dawugui 2010-11-04
  • 打赏
  • 举报
回复
针对SQL SERVER来说,除了用存储过程或游标,没有好的方法。
或者这种东西还不如直接在程序里面去做,获取到这个逻辑表达式,然后动态执行即可。
「已注销」 2010-11-04
  • 打赏
  • 举报
回复
思路就像4#的,别的也没想出来。
这样的做法貌似笨了点,不过很奏效,也很容易想到。
主要就是处理表达式中的逻辑运算符,
有真值就返回1,否则返回0,再跟where条件中的1比较就能实现要求了。

====
提一点建议:可以把替换变量@p作为参数传到function中去处理,不要写死了。
类似:create function fn_Check(@condition varchar(30),@parameter VARCHAR(10), @par VARCHAR(10))
华夏小卒 2010-11-04
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 vidor 的回复:]
SQL code
if object_id('test') is not null drop table test
go

create table test(Id int, Name varchar(10), Condition varchar(20))
insert into test select 1, 'A1', '@P < 1'
insert into test select……
[/Quote]高手啊,把第二个改改也加到第一个里面
Vidor 2010-11-04
  • 打赏
  • 举报
回复
if object_id('test') is not null drop table test
go

create table test(Id int, Name varchar(10), Condition varchar(20))
insert into test select 1, 'A1', '@P < 1'
insert into test select 2, 'A2', '@P < 4 AND @P >1'
insert into test select 3, 'A3', '@P >=3 OR @P < 2'
insert into test select 4, 'A4', '@P = 5'
go

---------------------------
-- 处理单个逻辑的函数
-- 比如: @P > 1
---------------------------
create function fn_base(@con varchar(20), @par int)
returns bit
as
begin
declare @1 int, @2 int
set @con = replace(@con, '@P', ltrim(@par))
if @con like '%>=%'
begin
select @1 = left(@con, charindex('>',@con)-1), @2 = stuff(@con, 1, charindex('=',@con), '')
if @1 >= @2 return 1
else return 0
end
if @con like '%<=%'
begin
select @1 = left(@con, charindex('<',@con)-1), @2 = stuff(@con, 1, charindex('=',@con), '')
if @1 <= @2 return 1
else return 0
end
if @con like '%>%'
begin
select @1 = left(@con, charindex('>',@con)-1), @2 = stuff(@con, 1, charindex('>',@con), '')
if @1 > @2 return 1
else return 0
end
if @con like '%<%'
begin
select @1 = left(@con, charindex('<',@con)-1), @2 = stuff(@con, 1, charindex('<',@con), '')
if @1 < @2 return 1
else return 0
end
return null
end
go

---------------------------
-- 只考虑两个逻辑条件的情况
-- 复杂逻辑自己扩展了
---------------------------
create function fn_Check(@con varchar(100), @par int)
returns bit
as
begin
if @con like '% AND %'
begin
set @con = replace(@con, ' AND ', '.')
if dbo.fn_base(parsename(@con,2), @par) = 0 return 0
if dbo.fn_base(parsename(@con,1), @par) = 0 return 0
else return 1
end
if @con like '% OR %'
begin
set @con = replace(@con, ' OR ', '.')
if dbo.fn_base(parsename(@con,2), @par) = 1 or dbo.fn_base(parsename(@con,1), @par) = 1 return 1
else return 0
end
return null
end
go


-- Check --
select * from test where dbo.fn_Check(Condition, 2) = 1
/*
Id Name Condition
----------- ---------- --------------------
2 A2 @P < 4 AND @P >1

(1 行受影响)

*/

-- 删除
/*
drop function fn_Check
drop function fn_base
drop table test
*/
王向飞 2010-11-04
  • 打赏
  • 举报
回复
没看明白
黄_瓜 2010-11-04
  • 打赏
  • 举报
回复
只要一个参数2? 对所有公式都判断?
华夏小卒 2010-11-04
  • 打赏
  • 举报
回复
这个

34,588

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server相关内容讨论专区
社区管理员
  • 基础类社区
  • 二月十六
  • 卖水果的net
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧