我在这里整理了问题的简化版本。LEFT JOIN导致重复的结果
方案
- 我的应用程序的用户,文件和文件夹。
- 用户可以创建只有他可以看到并共享所有用户才能看到的文件的私人文件。
- 用户可以创建私人文件夹,以便将其私人文件和共享文件组织到中。文件夹分配是可选的。如果用户未分配文件夹,则文件显示在“未分类”栏中。
架构
-- -------------------------------------
-- User
-- -------------------------------------
CREATE TABLE [User] (
[Id] VARCHAR(50) NOT NULL
);
INSERT INTO [User]
VALUES ('user_1');
INSERT INTO [User]
VALUES ('user_2');
-- -------------------------------------
-- Folder
-- -------------------------------------
CREATE TABLE [Folder] (
[Id] VARCHAR(50) NOT NULL,
[UserId] VARCHAR(50) NOT NULL
);
-- Each user has a private folder
INSERT INTO [Folder]
VALUES ('user1_folder', 'user_1');
INSERT INTO [Folder]
VALUES ('user2_folder', 'user_2');
-- -------------------------------------
-- File
-- -------------------------------------
CREATE TABLE [File] (
[Id] VARCHAR(50) NOT NULL,
[UserId] VARCHAR(50) NULL
);
-- Private files
INSERT INTO [File]
VALUES ('user1_file1', 'user_1');
INSERT INTO [File]
VALUES ('user1_file2', 'user_1');
INSERT INTO [File]
VALUES ('user2_file1', 'user_2');
INSERT INTO [File]
VALUES ('user2_file2', 'user_2');
-- Shared files
INSERT INTO [File]
VALUES ('shared_file1', NULL);
INSERT INTO [File]
VALUES ('shared_file2', NULL);
INSERT INTO [File]
VALUES ('shared_file3', NULL);
-- UPDATE: new case
INSERT INTO [File]
VALUES ('shared_file4', NULL);
-- -------------------------------------
-- FolderFile Association
-- -------------------------------------
CREATE TABLE [FolderFile] (
[FolderId] VARCHAR(50) NOT NULL,
[FileId] VARCHAR(50) NOT NULL
);
-- User 1 puts some files in his private folders
INSERT INTO [FolderFile]
VALUES ('user1_folder', 'user1_file');
INSERT INTO [FolderFile]
VALUES ('user1_folder', 'shared_file1');
INSERT INTO [FolderFile]
VALUES ('user1_folder', 'shared_file2');
-- User 2 puts some files in his private folders
INSERT INTO [FolderFile]
VALUES ('user2_folder', 'user2_file');
INSERT INTO [FolderFile]
VALUES ('user2_folder', 'shared_file1');
-- UPDATE: new case
INSERT INTO [FolderFile]
VALUES ('user2_folder', 'shared_file4');
所需的结果
我希望看到所有专用和共享文件给定@UserId
(user_1
在这种情况下),与相关的私人文件夹的用户一起(如果有的话)。 请注意,对于用户的文件,文件夹是可选的。
尝试查询#1
DECLARE @UserId VARCHAR(50) = 'user_1'
SELECT
F.[Id] AS [FileId],
F.[UserId] AS [FileUserId],
FO.[Id] AS [FolderId]
FROM
[File] AS F
LEFT JOIN
[FolderFile] FOF ON FOF.[FileId] = F.[Id]
LEFT JOIN
[Folder] FO ON FO.[Id] = FOF.[FolderId]
WHERE
F.[UserId] IS NULL
OR F.[UserId] = @UserId
结果#1
FileId FileUserId FolderId
=========================================
user1_file1 user_1 NULL
user1_file2 user_1 NULL
shared_file1 NULL user1_folder
shared_file1 NULL user2_folder <== bad result
shared_file2 NULL user1_folder
shared_file3 NULL NULL
shared_file4 NULL user2_folder <== bad result
尝试查询#2
添加另一个条件的Folder
JOIN
ON
。
DECLARE @UserId VARCHAR(50) = 'user_1'
SELECT
F.[Id] AS [FileId],
F.[UserId] AS [FileUserId],
FO.[Id] AS [FolderId]
FROM
[File] AS F
LEFT JOIN
[FolderFile] FOF ON FOF.[FileId] = F.[Id]
LEFT JOIN
[Folder] FO ON FO.[Id] = FOF.[FolderId] AND FO.[UserId] = @UserId -- Add another condition here on UserId
WHERE
F.[UserId] IS NULL
OR F.[UserId] = @UserId
结果#2
FileId FileUserId FolderId
=========================================
user1_file1 user_1 NULL
user1_file2 user_1 NULL
shared_file1 NULL user1_folder
shared_file1 NULL NULL <== bad result
shared_file2 NULL user1_folder
shared_file3 NULL NULL
shared_file4 NULL NULL
分析
正如你可以在上面看到,为user_2
的文件夹中的关联从而导致额外的一行user_1
归还。我不希望这一行被包含在内。
如果FolderFile
表有一个UserId
就可以了,我想我可以用一个条件限制它,但它没有。 UserId
暗示通过相关的Folder
。关联上的LEFT JOIN
会导致它传播null并传递它下面的条件。
我跑出来的想法,但它可能是明显的东西:)
更新#1
我增加了一个新的情况与shared_file4
,这是一个文件夹中为user_2
,但不user_1
。它应该包含在两个用户的结果中。
INSERT INTO [File]
VALUES ('shared_file4', NULL);
INSERT INTO [FolderFile]
VALUES ('user2_folder', 'shared_file4');
如果你只是为了表示_shared_文件的方式是分配NULL作为文件的所有者,并且有一个_shared_或_private_文件夹中没有表示,那么这将是很难告诉共享文件在某些文件夹中是私人的。使用'bit'来表示每个文件和文件夹的共享/私有状态可能会更好,并且可以让您通过为每个文件和文件夹分配一个'UserId'来独立于共享来跟踪所有权。 – HABO
不谈论表格设计,你能解释为什么在'INSERT INTO [FolderFile] VALUES('user2_folder','shared_file1');'? –
@ PhamX.Bach因为文件夹分配是私人的。查询只应该返回'user_1'的结果。 – kspearrin