我正在寻找快速解决方案,以便在我们构建新版本时解决遗留系统的问题。
概述
核对清单页面检查数据库是否为每个用户输入了一定数量的条目。然后,页面根据登录用户的类型,检查每个参考表的count
个条目,其中包含所需条目数(“RequiredRows”)。然后页面将迭代RequiredRows
的用户条目。
问题
显然,当返回的条目数小于RequiredRows
时,这会失败。
明显
我知道大多数人,就像我一样,会说要更新页面,不要以这种方式进行迭代。不幸的是,在这个时刻,如果没有详细说明,这是不可能的,而且更加复杂。
我需要什么
我需要一个可以根据RequiredCount
返回相同数量的记录或更多记录的查询。
以下是一个例子:
方案
NULL
的2行。NULL
的1行。NULL
的1行。数据库结构
Id User Type
-------------------
1 User1 1
2 User2 2
Id UserType RequiredRows
--------------------------------
1 Public 3
2 Private 2
Id UserId Entry
----------------------
1 1 Test
期望的结果
查询 User1 的条目数时,我需要以下结果。
EntryId
--------
1
null
null
查询 User2 时:
EntryId
--------
null
null
考虑
虽然这只是一个小样本数据,但所需条目的数量在0-50之间变化,具体取决于不同的用户类型。
答案 0 :(得分:1)
这可行。
DECLARE @User TABLE(UserID INT,UserName NVARCHAR(20), UserTypeID INT)
DECLARE @UserType TABLE(UserTypeID INT, UserTypeName NVARCHAR(50), RequiredEntries INT)
DECLARE @UserEntry TABLE(UserEntryID INT, UserID INT, EntryName NVARCHAR(50))
INSERT @User SELECT 1 , 'User1', 1
INSERT @User SELECT 2 , 'User2', 2
INSERT @UserType SELECT 1, 'Public', 3
INSERT @UserType SELECT 2, 'Private', 2
INSERT @UserEntry SELECT 1,1,'Test'
DECLARE @UserID INT = 1
;
WITH RequiredEntries AS(
SELECT UserTypeID, RowNumber=1, RequiredEntries FROM @UserType UT
UNION ALL
SELECT UserTypeID,RowNumber=RowNumber + 1,RequiredEntries FROM RequiredEntries IR WHERE IR.RowNumber < IR.RequiredEntries
),
UserEntries AS(
SELECT UserID,EntryNumber=COUNT(*)
FROM @UserEntry UE
GROUP BY UserID,EntryName
),
UserTotals AS(
SELECT UserID,TotalEntries=COUNT(*)
FROM @UserEntry UE
GROUP BY UserID
)
SELECT
EntryNumber
FROM
@User U
INNER JOIN RequiredEntries RE ON RE.UserTypeID=U.UserTypeID
LEFT OUTER JOIN UserEntries UE ON UE.UserID=U.UserID AND EntryNumber=RE.RowNumber
INNER JOIN UserTotals UT ON UT.UserID=U.UserID AND UT.TotalEntries > 0 --1 and 5
WHERE
U.UserID=@UserID
ORDER BY
RE.RowNumber
答案 1 :(得分:1)
这是一种较少关系但冗长的方法:
DECLARE @User TABLE (
Id INT, [User] VARCHAR(255), [UserTypeId] INT
)
INSERT INTO @User
VALUES (1,'User1',1),(2,'User2',1),(3,'User3',2),(4,'User4',1),(5,'User5',2),(6,'User6',3)
DECLARE @UserType TABLE (
Id INT, UserType VARCHAR(20), RequiredRows INT
)
INSERT INTO @UserType
VALUES (1,'Public',3),(2,'Private',2),(3,'Untrusted',50)
DECLARE @Entries TABLE (
Id INT IDENTITY(1,1), UserId INT, [Entry] VARCHAR(255)
)
INSERT INTO @Entries
VALUES (1,'Test'),(2,'Test'),(2,'Test 2'),(3,'Test')
, (5,'MoreTests1'),(5,'Test2'),(5,'Test 3'),(5,'Test 4')
, (6,'SomeTest1'),(6,'SomeTest2'),(6,'SomeTest3'),(6,'SomeTest4'),(6,'SomeTest5')
, (6,'SomeTest6'),(6,'SomeTest7'),(6,'SomeTest8'),(6,'SomeTest9'),(6,'SomeTest10')
DECLARE @UserId INT = 1
DECLARE @RequiredRows INT = (
SELECT RequiredRows
FROM @User u
INNER JOIN @UserType ut
ON u.UserTypeId = ut.Id
WHERE u.Id = @UserId)
DECLARE @ExistingRows INT = (SELECT COUNT(*) FROM @Entries WHERE UserId = @UserId)
DECLARE @MissingRows INT = (SELECT CASE WHEN @RequiredRows < @ExistingRows OR @ExistingRows = 0 THEN 0 ELSE @RequiredRows - @ExistingRows END)
;WITH fill AS (
SELECT EmptyId FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) gen(EmptyId)
)
SELECT Id as EntryId
FROM @Entries WHERE UserId = @UserId
UNION ALL
SELECT TOP(@MissingRows) a.EmptyId
FROM fill a, fill b, fill c
基本上它确定了所需的行,如果没有条目和现有行,则确定0。其余的是选择条目并填写,如有必要,使用空元组。
您可以针对方案 2 将@UserId
更改为1,针对方案 3 更新@UserId = 2
,针对方案@UserId = 3方案 1 > 6 和@UserId = 4
,但您可以轻松添加数据以涵盖其他测试用例。
编辑:添加了一些测试数据并重写了查询以最大限度地减少数据读取量。
<强> EDIT2:强> 这是一个紧凑的recursive cte,它使用ROW_NUMBER来枚举条目,并确保它具有所需的行数。
DECLARE @User TABLE (
Id INT, [User] VARCHAR(255), [UserTypeId] INT
)
INSERT INTO @User
VALUES (1,'User1',1),(2,'User2',1),(3,'User3',2),(4,'User4',1),(5,'User5',2),(6,'User6',3)
DECLARE @UserType TABLE (
Id INT, UserType VARCHAR(20), RequiredRows INT
)
INSERT INTO @UserType
VALUES (1,'Public',3),(2,'Private',2),(3,'Untrusted',50)
DECLARE @Entries TABLE (
Id INT IDENTITY(1,1), UserId INT, [Entry] VARCHAR(255)
)
INSERT INTO @Entries
VALUES (1,'Test'),(2,'Test'),(2,'Test 2'),(3,'Test')
, (5,'MoreTests1'),(5,'Test2'),(5,'Test 3'),(5,'Test 4')
, (6,'SomeTest1'),(6,'SomeTest2'),(6,'SomeTest3'),(6,'SomeTest4'),(6,'SomeTest5')
, (6,'SomeTest6'),(6,'SomeTest7'),(6,'SomeTest8'),(6,'SomeTest9'),(6,'SomeTest10')
DECLARE @UserId INT = 1
;WITH rcte AS (
SELECT ROW_NUMBER() OVER (ORDER BY e.Id) AS RN
, e.Id
, ut.RequiredRows
FROM @Entries e
INNER JOIN @User AS u ON e.UserId = u.Id
INNER JOIN @UserType AS ut ON u.UserTypeId = ut.Id
WHERE UserId = @UserId
UNION ALL
SELECT RN + 1, NULL, RequiredRows
FROM rcte r
WHERE r.RN + 1 <= RequiredRows
)
SELECT RN, MAX(Id) AS EntryId
FROM rcte
GROUP BY RN