web-dev-qa-db-ja.com

5つのデータベースインスタンスを介した6つのテーブルの複数のJOIN

私は現在、企業のユーザー管理をリエンジニアリングしており、5つのデータベースインスタンスすべてのすべてのユーザーをリストするテーブルを作成しています。次のステップは、すべてのインスタンスに対してユーザーが持つすべての役割を示すクエリを記述する必要があることです。

私はすでに_UNION ALL_を使用しましたが、出力は構造化されておらず、どのインスタンスでどのロールを使用しているかはわかりません。だから私はたった3つのテーブルで以下を試しました:

_SELECT W.GRANTED_ROLE "GRANTED_ROLE_DB1", V.GRANTED_ROLE "GRANTED_ROLE_DB2"
FROM SCHEMA.USR_ALL_USERS U
LEFT OUTER JOIN SYS.DBA_ROLE_PRIVS W
   ON (U.USERNAME = W.GRANTEE AND U.DB_INSTANCE = 'DB1')
LEFT OUTER JOIN SYS.DBA_ROLE_PRIVS@DB2_LINK V
   ON (U.USERNAME = V.GRANTEE AND U.DB_INSTANCE = 'DB2')
WHERE U.USERNAME = 'USER'
ORDER BY U.USERNAME ASC;
_

実際には機能しましたが、出力は満足のいくものではありませんでした:

_GRANTED_ROLE_DB1            GRANTED_ROLE_DB2
--------------------------- --------------------------
ROLE_1
ROLE_2
ROLE_3
ROLE_4
                            ROLE_1
                            ROLE_2
                            ROLE_4
                            ROLE_5
                            ROLE_6
_

このような出力をする方法はありますか?

_GRANTED_ROLE_DB1            GRANTED_ROLE_DB2
--------------------------- --------------------------
ROLE_1                      ROLE_1
ROLE_2                      ROLE_2
ROLE_3
ROLE_4                      ROLE_4
                            ROLE_5
                            ROLE_6
_

私はON ((U.USERNAME = V.GRANTEE OR W.GRANTED_ROLE = V.GRANTED_ROLE) AND U.DB_INSTANCE = 'DB2')を試しましたが、出力はさらに悪かったです。

あなたたちは何か提案や役立つ考えがありますか?

4
Chris.V

SCHEMA.USR_ALL_USERSテーブルは実際にこのクエリに参加する必要がありますか?それはそうではないようです:結果をフィルタリングしている同じ列USERNAMEは、他の2つのテーブルを結合するためにも使用されます。したがって、SCHEMA.USR_ALL_USERSを省略して、SYS.DBA_ROLE_PRIVSSYS.DBA_ROLE_PRIVS@DB2_LINKに直接フィルタリングを適用できます。

そして今、これらの2つのテーブルの2つのサブセットがあるので、 @ Chris Saxon's アプローチを取り、FULL JOINを使用することができます。

SELECT
  DB1.GRANTED_ROLE AS "GRANTED_ROLE_DB1",
  DB2.GRANTED_ROLE AS "GRANTED_ROLE_DB2"
FROM (
  SELECT GRANTED_ROLE
  FROM SYS.DBA_ROLE_PRIVS
  WHERE GRANTEE = 'USER'
) DB1
FULL OUTER JOIN (
  SELECT GRANTED_ROLE
  FROM SYS.DBA_ROLE_PRIVS@DB2_LINK
  WHERE GRANTEE = 'USER'
) DB2
ON DB1.GRANTED_ROLE = DB2.GRANTED_ROLE
ORDER BY
  COALESCE(DB1.GRANTED_ROLE, DB2.GRANTED_ROLE)
;

または、2つのセットを結合することもできます。

SELECT
  'GRANTED_ROLE_DB1' AS GRANTED_ROLE_DB,
  GRANTED_ROLE
FROM SYS.DBA_ROLE_PRIVS
WHERE GRANTEE = 'USER'
UNION ALL
SELECT
  'GRANTED_ROLE_DB2' AS GRANTED_ROLE_DB,
  GRANTED_ROLE
FROM SYS.DBA_ROLE_PRIVS@DB2_LINK
WHERE GRANTEE = 'USER'

このような結果を得る:

GRANTED_ROLE_DB   GRANTED_ROLE
----------------  ------------
GRANTED_ROLE_DB1  ROLE_1
GRANTED_ROLE_DB1  ROLE_2
GRANTED_ROLE_DB1  ROLE_3
GRANTED_ROLE_DB1  ROLE_4
GRANTED_ROLE_DB2  ROLE_1
GRANTED_ROLE_DB2  ROLE_2
GRANTED_ROLE_DB2  ROLE_4
GRANTED_ROLE_DB2  ROLE_5
GRANTED_ROLE_DB2  ROLE_6

次にPIVOTします。このような:

SELECT
  CASE WHEN "'GRANTED_ROLE_DB1'" > 0 THEN GRANTED_ROLE END AS GRANTED_ROLE_DB1,
  CASE WHEN "'GRANTED_ROLE_DB2'" > 0 THEN GRANTED_ROLE END AS GRANTED_ROLE_DB2
FROM (
  SELECT
    'GRANTED_ROLE_DB1' AS GRANTED_ROLE_DB,
    GRANTED_ROLE
  FROM SYS.DBA_ROLE_PRIVS
  WHERE GRANTEE = 'USER'
  UNION ALL
  SELECT
    'GRANTED_ROLE_DB2' AS GRANTED_ROLE_DB,
    GRANTED_ROLE
  FROM SYS.DBA_ROLE_PRIVS@DB2_LINK
  WHERE GRANTEE = 'USER'
) s
PIVOT (
  COUNT(*) FOR GRANTED_ROLE_DB IN ('GRANTED_ROLE_DB1', 'GRANTED_ROLE_DB2')
) p
ORDER BY
  GRANTED_ROLE
;

「動作中」の両方のアプローチを見ることができます SQL Fiddle

3
Andriy M

問題は、インスタンスフィールドを制限しているため、ロールが一方または他方の列にしか表示されないことです。

これを回避するには、次のように、2番目のデータベースから最初のデータベースの役割へのdb_instanceおよびfull outer join役割の制限を削除します。

SELECT W.GRANTED_ROLE "GRANTED_ROLE_DB1", V.GRANTED_ROLE "GRANTED_ROLE_DB2"
FROM SCHEMA.USR_ALL_USERS U
LEFT OUTER JOIN SYS.DBA_ROLE_PRIVS W
   ON (U.USERNAME = W.GRANTEE)
FULL OUTER JOIN SYS.DBA_ROLE_PRIVS@DB2_LINK V
   ON (U.USERNAME = V.GRANTEE AND V.GRANTED_ROLE = W.GRANTED_ROLE)
WHERE nvl(U.USERNAME, v.username) = 'USER'
ORDER BY nvl(W.GRANTED_ROLE, V.GRANTED_ROLE) ASC;

この SQLFiddle には実用的な例があります。 Andriyからのコメントに続いて fiddle を更新。

3
Chris Saxon