0

クエリを最適化する方法について質問があります。実際、クエリを頻繁に実行する予定なので、具体化されたビューまたはインデックス付きビューを使用するか (これは良い考えですか?)、非正規化することを考えていました。

次の 4 つのテーブルを考えてみましょう (無関係なフィールドは省略されています)。

  • ユーザー (int userId)
  • グループ (int groupId)
  • GroupMemberships (int userId、int groupId、bool isSharing)
  • コンピュータ (int userId)

この関係は、ユーザーが 0..n 台のコンピューター (1 人のユーザーから多数のコンピューター) を持つことができ、0..n グループのメンバーになることができるということです。グループには 0..n 人のユーザー (多数のユーザーから多数のグループ) を含めることができます。「isSharing」は、ユーザーがそのグループと共有しているか、そのグループの「読み取り専用」メンバーであるかを示します (つまり、共有メンバーのコンピューターを表示できますが、自分のコンピューターは共有しません)。

クエリは、特定のユーザーについて、そのユーザーが表示できるコンピューターを見つけることです。ユーザーは、自分のすべてのコンピューターを表示できます。彼女はまた、彼女がメンバーであり、そのグループと共有しているグループに属している他のユーザーのコンピューターも見ることができます。さて、それはあまり意味がないので、O(n^3) 疑似コードの目標は次のとおりです。

List<Computer> l
foreach(Computer c in Computers)
    if(c.userId == current_user_id)
        add c to l
    else
        foreach(GroupMembership m where m.userId == current_user_id)
            foreach(GroupMembership m2 where c.userId == m2.userId && m.groupId == m2.groupId)
                if(m2.isSharing)
                    add c to l

現在、私は ORM マッパーを使用しており、基本的に上記のことを行っています (SQL のことはあまり得意ではありません) が、それは明らかに理想的とは言えない解決策です。そこにリストしたすべてのフィールド (isShared を除く) にインデックスがあり、GroupMembership の (userId, groupId) タプルに追加のインデックスがあります。しかし、データベースの専門家がより良い解決策を思い付くことができるでしょうか?

プロジェクトはまだ稼働していませんが、ユーザーあたり平均 1.2 台のコンピューター (全員が 1 台、一部はそれ以上のコンピューターを所有する可能性があります) であり、ユーザーあたりのグループ メンバーシップはおそらく 0.75 になると思います (多くのユーザーはグループを使用しません)。機能ですが、そうする人は複数のグループのメンバーになる可能性があります)。また、これらの関連付けられたテーブルはすべて頻繁に追加されるため、マテリアライズド ビューの実用性が低下する可能性があります。SQL Server 2008 を使用しています。

ありがとう、万歳、ロバート

4

2 に答える 2

1

OK、上記の仕様のテーブルとクエリが必要だと思いますか?

コンピューターが特定のユーザーに「割り当てられている」という仕様から取得しましたが、共有できますか?

コンピューター(int userId)

これを見て、仕様を変更したい場合はお知らせください。

DECLARE @Users TABLE(
        UserID INT
)

DECLARE @Computers TABLE(
        ComputerID INT,
        UserID INT
)

DECLARE @Groups TABLE(
        GroupID INT
)

DECLARE @GroupMemberships TABLE(
        UserID INT,
        GroupID INT,
        IsSharing INT
)

INSERT INTO @Users (UserID) SELECT 1
INSERT INTO @Users (UserID) SELECT 2

INSERT INTO @Computers (ComputerID, UserID) SELECT 1, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 2, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 3, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 4, 2
INSERT INTO @Computers (ComputerID, UserID) SELECT 5, 2

INSERT INTO @Groups (GroupID) SELECT 1
INSERT INTO @Groups (GroupID) SELECT 2
INSERT INTO @Groups (GroupID) SELECT 3

INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 1, 1, 0
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 1, 2, 1
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 2, 2, 0
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 2, 3, 0

DECLARE @UserID INT
--SELECT @UserID = 1
SELECT @UserID = 2

SELECT  DISTINCT 
        ComputerID
FROM    @Computers
WHERE   UserID = @UserID
UNION
SELECT  DISTINCT 
        ComputerID
FROM    @Computers c INNER JOIN
        (
            SELECT  DISTINCT 
                    gm.UserID
            FROM    @GroupMemberships gm INNER JOIN
                    @GroupMemberships ThisUserGroups    ON  gm.GroupID = ThisUserGroups.GroupID
                                                        AND ThisUserGroups.UserID = @UserID
            WHERE   gm.UserID != @UserID
            AND             gm.IsSharing = 1
    ) OtherUsersInSharedGroups ON c.UserID = OtherUsersInSharedGroups.UserID
于 2009-09-30T08:26:32.833 に答える
1

これは、サブクエリなしで実行できると思います。免責事項: これは私の思いつきであり、テストされていません。

select distinct computerId
from groupMemberships m1
join groupMemberships m2 on m2.groupId=m1.groupId
  and (m2.isSharing or m2.userId=m1.userId)
join computers c on c.userId=m2.userId
where m1.userId=?

言及していない選択に含めたいテーブルの他のデータがない限り、ユーザーテーブルのグループを読み取る必要はありません。

「isSharing または userId」は、自分のコンピューターと共有コンピューターを取得する必要があります。これは不必要に賢いかもしれません: 単純な和集合の方が効果的かもしれません。

于 2009-10-01T21:05:11.797 に答える