有更好的方法吗?
下面的SQL Server 2008 R2表值函数运行查询以确定与特定条件匹配的记录数,然后根据第一个查询的结果执行另一个查询。
这是正确的做法吗?或者有更好的方法来实现相同的结果吗?
USE [My_MSSQL_Database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[GetActiveProfileIDForSystemID](@SystemID NVARCHAR(10))
RETURNS @retActiveProfileID TABLE
(
ActiveUserProfile NVARCHAR(10)
)
AS
BEGIN
DECLARE @ProfileCount NVARCHAR(50);
DECLARE @ProfilesLookup NVARCHAR(50);
SELECT @ProfileCount = COUNT(mdl_user.id)
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 2)
AND (mdl_user_info_data.data LIKE @SystemID)
AND mdl_user.id NOT IN (
SELECT mdl_user.id
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 4)
AND (mdl_user_info_data.data LIKE 'Yes')
AND (mdl_user.deleted = 0)
)
AND (mdl_user.deleted = 0);
IF (@ProfileCount > 0)
BEGIN
SELECT @ProfilesLookup = mdl_user.id
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 2)
AND (mdl_user_info_data.data LIKE @SystemID)
AND mdl_user.id NOT IN (
SELECT mdl_user.id
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 4)
AND (mdl_user_info_data.data LIKE 'Yes')
AND (mdl_user.deleted = 0)
)
AND (mdl_user.deleted = 0)
END
ELSE
BEGIN
SELECT @ProfilesLookup = mdl_user.id
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 2)
AND (mdl_user_info_data.data LIKE @SystemID)
AND (mdl_user.deleted = 0)
END
IF @ProfilesLookup IS NOT NULL
BEGIN
INSERT @retActiveProfileID
SELECT @ProfilesLookup;
END
RETURN
END
mdl_user_info_data.userid是mdl_user.id上隐含的FK (mdl_user.id = mdl_user_info_data.userid)
基本上,我想做的是:
运行一个查询,该查询将计算mdl_user_info_data表中有多少行/记录
WHERE (mdl_user_info_data.fieldid = 4)
AND (mdl_user_info_data.data LIKE 'Yes')
和 WHERE(mdl_user_info_data.fieldid = 2) AND(mdl_user_info_data.data LIKE @SystemID)
如果没有行(COUNT = 0),那么我想:
SELECT id
FROM mdl_user_info_data
WHERE mdl_user_info_data.userid = @SystemID
其中@SystemID是在调用时传递给GetActiveProfileIDForSystemID(@SystemID)函数的@SystemID值。
在 mdl_user_info_data.fieldid = 2和 mdl_user_info_data.fieldid = 4 行是应用程序(Moodle)中的“用户配置文件”字段。
mdl_user_info_data表定义为:
CREATE TABLE [dbo].[mdl_user_info_data](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[userid] [bigint] NOT NULL,
[fieldid] [bigint] NOT NULL,
[data] [ntext] NOT NULL,
[dataformat] [smallint] NOT NULL,
CONSTRAINT [mdl_userinfodata_id_pk] PRIMARY KEY CLUSTERED
)
答案 0 :(得分:1)
这取决于你对“更好”的意思。
从易于理解和维护的角度来看,目前的变体可能没问题。
从性能的角度来看,它很可能会得到改进,因为你执行了两次相同的查询。
这是一种可行的方法。这是我对这个功能背后的逻辑的理解。
有两组可能相交的ID。
A:
SELECT ID
FROM T
WHERE <ExpressionA>
SELECT mdl_user.id
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 2)
AND (mdl_user_info_data.data LIKE @SystemID)
AND (mdl_user.deleted = 0)
B:
SELECT ID
FROM T
WHERE <ExpressionB>
SELECT mdl_user.id
FROM mdl_user
INNER JOIN mdl_user_info_data ON mdl_user.id = mdl_user_info_data.userid
WHERE (mdl_user_info_data.fieldid = 4)
AND (mdl_user_info_data.data LIKE 'Yes')
AND (mdl_user.deleted = 0)
如果设置
A
减去setB
有任何行,那么我们返回一个随机行 从差异ELSE返回集合A
中的一个随机行。
这两套看起来都是这样的:
A: |........................|
B: |,,,,,,,,,,,,,,,,,,,,,,,,|
|................|;;;;;;;|,,,,,,,,,,,,,,,,|
A - B A x B B - A
如果
A-B
不为空,则返回A-B
ELSE返回A
或AxB
(如果A-B
是空的,然后是A = AxB
)
我们只想返回一行,而不是整个行。因此,我们可以返回A-B
加AxB
的有序联合,并获取结果的第一行。
WITH
CTE_A
AS
(
SELECT ID
FROM T
WHERE <ExpressionA>
)
,CTE_B
AS
(
SELECT ID
FROM T
WHERE <ExpressionB>
)
,CTE_OrderedA
AS
(
-- A - B
SELECT 1 AS SortOrder, ID
FROM CTE_A
WHERE ID NOT IN
(
SELECT ID
FROM CTE_B
)
UNION ALL
-- A x B
SELECT 2 AS SortOrder, ID
FROM CTE_A
WHERE ID IN
(
SELECT ID
FROM CTE_B
)
)
SELECT TOP(1) ID
FROM CTE_OrderedA
ORDER BY SortOrder
;
此查询消除了对显式IF
的需要。现在,您可以简化函数本身并使其成为inline TVF。
它应该比当前变体更有效,但您应该使用实际数据来衡量实际性能。
实际上,可以说从维护的角度来看单个查询变体也更好,因为查询的代码不会重复。 但这背后的逻辑可能并不明显。 在任何情况下都应该有适当的评论解释逻辑。