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;