SQL 服务器中的函数
Functions in SQL Server
我们正在将数据库的后端从 Access 移动到 SQL 服务器,请求帮助了解如何在 SQL 服务器中实现我们的用户定义函数以前在 Access 中使用。 (或者,如果我们正在做的事情有更直接的解决方案,比如使用比我熟悉的更复杂的 SQL 语句,那也很好。)
所以我们目前有一个如下所示的查询:
SELECT StaffLine(Docket, Lead, Reviewer) AS OfficeStaff, Docket, FiledDate, Lead, Reviewer, Status, [a lot of other fields]
FROM tCases
WHERE Status = 1;
这里,OfficeStaff 是从 StaffLine 函数计算出来的。 StaffLine 是一个函数,它只是构建所有分配的员工的完整声明,从存储数据的两个不同 tables 中提取。主要团队成员(团队负责人和审阅者)存储在主要 table (tCases) 中。然后所有其他团队成员的姓名都存储在一个名为 tMembers 的相关 table 中。因此 OfficeStaff 的值通常如下所示:
Office Staff: John Doe (lead), Bob Jones, Billy Bob, Pat Jones, Jane Doe (reviewer)
我们想实现一个类似于我们的 StaffLine 功能的功能,但是在 SQL 服务器中。我在下面粘贴了我们的 StaffLine 函数。我已经对如何在我们的 SQL Server Management Studio 中使用可编程性构建用户定义的函数进行了一些研究,但我还没有能够从我找到的文档中获得足够的意义。因此,非常感谢任何有关在 Management Studio 中实现该功能时需要什么以及我将把它放在哪里的帮助。
Access 中的当前 VBA 用户定义函数:
Public Function StaffLine(Docket As String, _
Lead As Variant, _
Reviewer As Variant _
) As String
' Lead and Reviewer are Variants because they are sometimes Null, and String can't handle Nulls.
' Start off building string by adding text for Lead
StaffLine = "Office Staff: " & Lead & " (lead), "
' Next add text for any non-lead team members
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT MemberName FROM tMembers WHERE mDocket = '" & Docket & "'")
' Check to see if the recordset actually contains rows
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF = True
StaffLine = StaffLine & rs!MemberName & ", "
'Move to the next record.
rs.MoveNext
Loop
End If
' Finally, add text for reviewer
StaffLine = StaffLine & Reviewer & " (reviewer)"
End Function
这是 "SQL Way" 的做法。
首先创建这个视图:
CREATE VIEW Staff_String AS
(
SELECT Docket, 'Office Staff : ' || COALESCE(C.Lead || ' (lead),','') ||
STRING_AGG(M.MemberName,', ') ||
C.Reviewer || '(reviewer)' as OfficeStaff
FROM tCases C
JOIN tMembers M ON M.mDocket = C.Docket
GROUP BY C.Docket, C.Lead, C.Reviewer
)
在 SQL 中,此视图的作用类似于函数或映射 -- 它定义了如何为所有案卷编号创建员工字符串。
在SQL中我们喜欢处理集合——在SQL中我们永远不应该用循环迭代,循环是邪恶的!
然后做你的 select 你只需加入它:
SELECT Staff_String.OfficeStaff, Docket, FiledDate, Lead, Reviewer, Status, [a lot of other fields]
FROM tCases
LEFT JOIN Staff_string ON tCases.Docket = Staff_string.Docket
WHERE tCases.Status = 1;
哒哒!简单。
这是一个使用 xml 和交叉应用的完整工作示例:
declare @tCases table (
tCasesID int identity(1,1) not null primary key clustered
, docketID int not null
, staffID int not null
, GivenName nvarchar(255) not null
, SurName nvarchar(255) not null
, Role_ nvarchar(255) not null
)
insert into @tCases (docketID, staffID, GivenName, SurName, Role_)
select 1, 100, 'bob', 'richards', 'reviewer' union all
select 1, 110, 'john', 'doe', 'lead' union all
select 1, 112, 'jane', 'doe', 'reviewer';
declare @tMember table (
tMemberID int identity(1,1) not null primary key clustered
, docketID int not null
, staffID int not null
, GivenName nvarchar(255) not null
, SurName nvarchar(255) not null
);
insert into @tMember (docketID, staffID, GivenName, SurName)
select 1, 133, 'Mary', 'Jones' union all
select 1, 134, 'Tom', 'Jones' union all
select 1, 105, 'Jimmy', 'Jon' union all
select 1, 109, 'Marsha', 'Marsha';
;with cte as (
select docketid
, GivenName
, SurName
from @tCases
union all
select docketid
, GivenName
, SurName
from @tMember
)
select distinct a.docketID
, x.list
from @tCases a
cross apply (
select stuff
(
(select ', ' +GivenName + ' ' + SurName
from cte b
where a.docketid = b.docketid
for xml path ('')
) , 1, 1, '') as list ) as x
这是结果集:
docketID list
1 bob richards, john doe, jane doe, Mary Jones, Tom Jones, Jimmy Jon, Marsha Marsha
这是创建函数的方法,您需要使用变量的实际大小更新参数的 "max" 大小
CREATE FUNCTION [dbo].[StaffLine](@Docket varchar(max), @Lead Varchar(max) = null, @Reviewer Varchar(Max) = null) RETURNS varChar(max)
AS
BEGIN
Declare @StaffLine as varChar(max) = ''
Declare @Temp TABLE (ID int identity, MemberName varchar(100))
Declare @row_count as int = 1
Declare @total_records as int
--Fill hte table to loop
Insert Into @Temp
SELECT MemberName
FROM tMembers WHERE mDocket = @Docket
Set @StaffLine = 'Office Staff: ' + ISNULL(@Lead, '') + ' (lead), '
Set @total_records = (Select Count(*) from @Temp) -- Get total records to loop
While @row_count <= @total_records
Begin
Set @StaffLine += (Select MemberName + ', '
From @Temp Where ID = @row_count)
Set @row_count += 1
End
SET @StaffLine += ISNULL(@Reviewer, '') + ' (reviewer)'
RETURN @StaffLine
END
那你就这样用吧:
SELECT dbo.StaffLine(Docket, Lead, Reviewer) AS OfficeStaff, Docket, FiledDate, Lead, Reviewer, Status, [a lot of other fields]
FROM tCases
WHERE Status = 1;
我们正在将数据库的后端从 Access 移动到 SQL 服务器,请求帮助了解如何在 SQL 服务器中实现我们的用户定义函数以前在 Access 中使用。 (或者,如果我们正在做的事情有更直接的解决方案,比如使用比我熟悉的更复杂的 SQL 语句,那也很好。)
所以我们目前有一个如下所示的查询:
SELECT StaffLine(Docket, Lead, Reviewer) AS OfficeStaff, Docket, FiledDate, Lead, Reviewer, Status, [a lot of other fields]
FROM tCases
WHERE Status = 1;
这里,OfficeStaff 是从 StaffLine 函数计算出来的。 StaffLine 是一个函数,它只是构建所有分配的员工的完整声明,从存储数据的两个不同 tables 中提取。主要团队成员(团队负责人和审阅者)存储在主要 table (tCases) 中。然后所有其他团队成员的姓名都存储在一个名为 tMembers 的相关 table 中。因此 OfficeStaff 的值通常如下所示:
Office Staff: John Doe (lead), Bob Jones, Billy Bob, Pat Jones, Jane Doe (reviewer)
我们想实现一个类似于我们的 StaffLine 功能的功能,但是在 SQL 服务器中。我在下面粘贴了我们的 StaffLine 函数。我已经对如何在我们的 SQL Server Management Studio 中使用可编程性构建用户定义的函数进行了一些研究,但我还没有能够从我找到的文档中获得足够的意义。因此,非常感谢任何有关在 Management Studio 中实现该功能时需要什么以及我将把它放在哪里的帮助。
Access 中的当前 VBA 用户定义函数:
Public Function StaffLine(Docket As String, _
Lead As Variant, _
Reviewer As Variant _
) As String
' Lead and Reviewer are Variants because they are sometimes Null, and String can't handle Nulls.
' Start off building string by adding text for Lead
StaffLine = "Office Staff: " & Lead & " (lead), "
' Next add text for any non-lead team members
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT MemberName FROM tMembers WHERE mDocket = '" & Docket & "'")
' Check to see if the recordset actually contains rows
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF = True
StaffLine = StaffLine & rs!MemberName & ", "
'Move to the next record.
rs.MoveNext
Loop
End If
' Finally, add text for reviewer
StaffLine = StaffLine & Reviewer & " (reviewer)"
End Function
这是 "SQL Way" 的做法。
首先创建这个视图:
CREATE VIEW Staff_String AS
(
SELECT Docket, 'Office Staff : ' || COALESCE(C.Lead || ' (lead),','') ||
STRING_AGG(M.MemberName,', ') ||
C.Reviewer || '(reviewer)' as OfficeStaff
FROM tCases C
JOIN tMembers M ON M.mDocket = C.Docket
GROUP BY C.Docket, C.Lead, C.Reviewer
)
在 SQL 中,此视图的作用类似于函数或映射 -- 它定义了如何为所有案卷编号创建员工字符串。
在SQL中我们喜欢处理集合——在SQL中我们永远不应该用循环迭代,循环是邪恶的!
然后做你的 select 你只需加入它:
SELECT Staff_String.OfficeStaff, Docket, FiledDate, Lead, Reviewer, Status, [a lot of other fields]
FROM tCases
LEFT JOIN Staff_string ON tCases.Docket = Staff_string.Docket
WHERE tCases.Status = 1;
哒哒!简单。
这是一个使用 xml 和交叉应用的完整工作示例:
declare @tCases table (
tCasesID int identity(1,1) not null primary key clustered
, docketID int not null
, staffID int not null
, GivenName nvarchar(255) not null
, SurName nvarchar(255) not null
, Role_ nvarchar(255) not null
)
insert into @tCases (docketID, staffID, GivenName, SurName, Role_)
select 1, 100, 'bob', 'richards', 'reviewer' union all
select 1, 110, 'john', 'doe', 'lead' union all
select 1, 112, 'jane', 'doe', 'reviewer';
declare @tMember table (
tMemberID int identity(1,1) not null primary key clustered
, docketID int not null
, staffID int not null
, GivenName nvarchar(255) not null
, SurName nvarchar(255) not null
);
insert into @tMember (docketID, staffID, GivenName, SurName)
select 1, 133, 'Mary', 'Jones' union all
select 1, 134, 'Tom', 'Jones' union all
select 1, 105, 'Jimmy', 'Jon' union all
select 1, 109, 'Marsha', 'Marsha';
;with cte as (
select docketid
, GivenName
, SurName
from @tCases
union all
select docketid
, GivenName
, SurName
from @tMember
)
select distinct a.docketID
, x.list
from @tCases a
cross apply (
select stuff
(
(select ', ' +GivenName + ' ' + SurName
from cte b
where a.docketid = b.docketid
for xml path ('')
) , 1, 1, '') as list ) as x
这是结果集:
docketID list
1 bob richards, john doe, jane doe, Mary Jones, Tom Jones, Jimmy Jon, Marsha Marsha
这是创建函数的方法,您需要使用变量的实际大小更新参数的 "max" 大小
CREATE FUNCTION [dbo].[StaffLine](@Docket varchar(max), @Lead Varchar(max) = null, @Reviewer Varchar(Max) = null) RETURNS varChar(max)
AS
BEGIN
Declare @StaffLine as varChar(max) = ''
Declare @Temp TABLE (ID int identity, MemberName varchar(100))
Declare @row_count as int = 1
Declare @total_records as int
--Fill hte table to loop
Insert Into @Temp
SELECT MemberName
FROM tMembers WHERE mDocket = @Docket
Set @StaffLine = 'Office Staff: ' + ISNULL(@Lead, '') + ' (lead), '
Set @total_records = (Select Count(*) from @Temp) -- Get total records to loop
While @row_count <= @total_records
Begin
Set @StaffLine += (Select MemberName + ', '
From @Temp Where ID = @row_count)
Set @row_count += 1
End
SET @StaffLine += ISNULL(@Reviewer, '') + ' (reviewer)'
RETURN @StaffLine
END
那你就这样用吧:
SELECT dbo.StaffLine(Docket, Lead, Reviewer) AS OfficeStaff, Docket, FiledDate, Lead, Reviewer, Status, [a lot of other fields]
FROM tCases
WHERE Status = 1;