4

次のビュー「user_details_merged」を作成しました。

SELECT DISTINCT
coalesce(own.user_name, join_user_name.user_name) AS user_name,
coalesce(own.email, join_mail.email) AS email,
coalesce(own.first_name, join_name.first_name) AS first_name,
coalesce(own.last_name, join_name.last_name) AS last_name
FROM
user_details AS own

LEFT JOIN user_details AS join_user_name ON 
    own.user_name IS NULL AND (
    (join_user_name.email = own.email AND own.email IS NOT NULL) 
    OR (join_user_name.first_name = own.first_name AND join_user_name.last_name = own.last_name 
    AND own.first_name IS NOT NULL AND own.last_name IS NOT NULL))


LEFT JOIN user_details AS join_mail ON 
    own.email IS NULL AND (
    (join_mail.user_name = own.user_name AND own.user_name IS NOT NULL) 
    OR (join_mail.first_name = own.first_name AND join_mail.last_name = own.last_name 
    AND own.first_name IS NOT NULL AND own.last_name IS NOT NULL))

LEFT JOIN user_details AS join_name ON 
    own.first_name IS NULL AND own.last_name IS NULL AND (
    (join_name.email = own.email AND own.email IS NOT NULL) 
    OR (join_name.user_name = own.user_name AND own.user_name IS NOT NULL))

ORDER BY user_name ASC,email ASC, first_name ASC, last_name ASC

これにより、次の列がマージされます。

user_name | email | first_name | last_name
a             b       NULL         NULL
NULL          b        c            d
a            NULL      e            f
NULL          x        y            z

user_name | email | first_name | last_name
a             b       NULL         NULL
NULL          b        c            d
a            NULL      e            f
NULL          x        y            z

a             b        c            d
a             b        e            f

私が欲しいのは:

user_name | email | first_name | last_name
NULL          x        y            z
a             b        c            d
a             b        e            f

より多くの情報を持つ同じデータを持つ行がある場合、ROWSを含むNULLはありませんが、より多くの情報を持つ他の行がない場合は、/ NULLxyz/を保持します。

ここでのこの2番目のビューは、私が必要としていることを正確に実行します。

SELECT DISTINCT a.user_name,a.email,a.first_name,a.last_name FROM
user_details_merged a
LEFT JOIN user_details_merged b
ON
(
    (
    a.user_name IS NOT NULL OR 
    NOT EXISTS (SELECT user_name FROM user_details_merged b WHERE b.user_name IS NOT NULL AND 
        b.email=ISNULL(a.email,b.email) AND 
        b.first_name=isnull(a.first_name,b.first_name) AND
        b.last_name=isnull(a.last_name,b.last_name))
    )

    AND

    (
    a.email IS NOT NULL OR 
    NOT EXISTS (SELECT email FROM user_details_merged b WHERE b.email IS NOT NULL AND 
        b.user_name=ISNULL(a.user_name,b.user_name) AND 
        b.first_name=isnull(a.first_name,b.first_name) AND
        b.last_name=isnull(a.last_name,b.last_name))
    )

    AND

    (
    (a.first_name IS NOT NULL AND a.last_name IS NOT NULL) OR 
    NOT EXISTS (SELECT email FROM user_details_merged b WHERE b.email IS NOT NULL AND 
        b.user_name=ISNULL(a.user_name,b.user_name) AND 
        b.email=ISNULL(a.email,b.email))
        -- AND b.first_name=isnull(a.first_name,b.first_name) AND b.last_name=isnull(a.last_name,b.last_name))
    )

    AND NOT (a.first_name = b.first_name AND a.last_name = b.last_name AND a.email = b.email AND a.user_name = b.user_name)

)

WHERE coalesce(b.user_name,b.email,b.first_name,b.last_name) IS NOT NULL

主な問題は、データの取得元であるuser_detailsビューが、さまざまなテーブルの多くの和集合で構成されていることです。ユーザー名と電子メールのみを含むものと、電子メールと名/姓などのみを含むものがあります。そのため、一意のキーがなく、UNIONSのためにビューのインデックスを作成できません。そのため、1時間以内に最後のビューを実行することは不可能です。現在の回避策は、user_details_mergedビューのデータを一時テーブルに格納し、上記の2番目のビューでこのテーブルのデータを使用できるようにするプロシージャです。そうすれば、8000行の実行時間を7秒に短縮できます。

他に何か提案はありますか?

どうもありがとうございます ;)

4

3 に答える 3

1

痛い!これは厄介なデータモデルです。最善の解決策は、データモデルを修正して、このような複雑なクエリが不要になるようにすることです。ただし、これはアプリケーションの依存関係によってさらに複雑になる傾向があるため、すでに楽しまれていると思います。

  • 4行のサンプルを使用して、別の解決策を考え出しました。
  • 次に、名前と名前の値のみを含む行をいくつか追加しました。これにより、上記のクエリで見落とされたシナリオが明らかになりました。
  • また、データモデルがそのようなシナリオをサポートしているように見えるため、同じ6行を12K行以上に複製しました。これにより、上記のクエリが2時間以上実行された後、最終的にあきらめて停止しました。
  • ソリューションに対して12K行を実行したところ、期待どおりの結果が1秒以内に返されました。

だから、それ以上の苦労なしに:

-- =================================================================================
-- BEGIN: SETUP TEST DATA
-- =================================================================================
SET NOCOUNT ON 

IF OBJECT_ID('user_details', 'U') IS NOT NULL DROP TABLE user_details;
GO

CREATE TABLE dbo.user_details (
    user_name   char(1) NULL,
    email       char(1) NULL,
    first_name  char(1) NULL,
    last_name   char(1) NULL
)
GO

INSERT dbo.user_details
SELECT * 
  FROM (
        SELECT * FROM dbo.user_details WHERE 1=2
        UNION ALL SELECT 'a',   'b',    NULL,   NULL
        UNION ALL SELECT NULL,  'b',    'c',    'd'
        UNION ALL SELECT 'a',   NULL,   'e',    'f'
        UNION ALL SELECT NULL,  'x',    'y',    'z'
        UNION ALL SELECT NULL,  NULL,   'y',    'z'
        UNION ALL SELECT NULL,  NULL,   'a',    'z'
       ) A
GO

--/*
-- TURN 6 ROWS INTO OVER 12K ROWS TO TEST PERFORMANCE
DECLARE @count int; SELECT @count = 0
WHILE @count < 11
  BEGIN 
    INSERT user_details
    SELECT * 
      FROM user_details

    SELECT @count = @count + 1
END
--*/
-- =================================================================================
-- END: SETUP TEST DATA
-- =================================================================================


-- =================================================================================
-- BEGIN: NEW SOLUTION FINAL: <1sec on 12288 rows
-- =================================================================================
IF OBJECT_ID('tempdb..#useremail', 'U') IS NOT NULL DROP TABLE #useremail;
IF OBJECT_ID('tempdb..#email', 'U') IS NOT NULL DROP TABLE #email;
IF OBJECT_ID('tempdb..#user', 'U') IS NOT NULL DROP TABLE #user;
IF OBJECT_ID('tempdb..#name', 'U') IS NOT NULL DROP TABLE #name;


-- GET YOUR UNIQUE user_name AND email KEY
SELECT DISTINCT A.user_name, A.email
  INTO #useremail
  FROM user_details A


-- GET YOUR UNIQUE email VALUES
SELECT DISTINCT A.email, A.first_name, A.last_Name
  INTO #email
  FROM user_details A
 WHERE A.email IS NOT NULL


-- GET YOUR UNIQUE user_name VALUES
SELECT DISTINCT A.user_name, A.first_name, A.last_Name
  INTO #user
  FROM user_details A
 WHERE A.user_name IS NOT NULL


-- GET YOUR UNIQUE first_name AND last_Name VALUES NOT PART OF THE KEY
SELECT DISTINCT A.first_name, A.last_Name
  INTO #name
  FROM user_details A
 WHERE A.first_name IS NOT NULL
   AND A.last_Name IS NOT NULL
   AND A.user_name IS NULL
   AND A.email IS NULL


-- CLEAN UP YOUR UNIQUE user_name AND email KEY
DELETE A
-- SELECT *
  FROM #useremail A
  JOIN (
        SELECT *
          FROM #useremail
         WHERE user_name IS NOT NULL
           AND email IS NOT NULL
       ) B
    ON (A.user_name = B.user_name AND A.email     IS NULL)
    OR (A.email     = B.email     AND A.user_name IS NULL)


-- CLEAN UP YOUR UNIQUE email VALUES
DELETE A
-- SELECT *
  FROM #email A
  JOIN (
        SELECT *
          FROM #email
         WHERE first_name IS NOT NULL
           AND last_Name IS NOT NULL
       ) B
    ON A.email = B.email
   AND A.first_name IS NULL
   AND A.last_name IS NULL


-- CLEAN UP YOUR UNIQUE user_name VALUES
DELETE A
-- SELECT *
  FROM #user A
  JOIN (
        SELECT *
          FROM #user
         WHERE first_name IS NOT NULL
           AND last_Name IS NOT NULL
       ) B
    ON A.user_name = B.user_name
   AND A.first_name IS NULL
   AND A.last_name IS NULL


-- CLEAN UP YOUR UNIQUE #name VALUES
DELETE A
-- SELECT *
  FROM #name A
  JOIN #user B
    ON A.first_name = B.first_name
   AND A.last_name  = B.last_name

DELETE A
-- SELECT *
  FROM #name A
  JOIN #email B
    ON A.first_name = B.first_name
   AND A.last_name  = B.last_name


-- GET YOUR DATA
SELECT A.user_name
      ,A.email
      ,U.first_name
      ,U.last_name
      --,*
  FROM #useremail A
  JOIN #user U
    ON A.user_name = U.user_name
 UNION 
SELECT A.user_name
      ,A.email
      ,E.first_name
      ,E.last_name
      --,*
  FROM #useremail A
  JOIN #email E
    ON A.email = E.email
 UNION 
SELECT NULL as [user_name]
      ,NULL as [email]
      ,N.first_name
      ,N.last_name
      --,*
  FROM #name N
-- =================================================================================
-- END: NEW SOLUTION FINAL
-- =================================================================================
于 2013-03-12T05:26:41.680 に答える
0

-今回は正しければ、次のようにして解決できます。

 SELECT ISNULL(A.USER_NAME, B.USER_NAME), A.EMAIL, A.FIRST_NAME, A.LAST_NAME FROM
 user_details A CROSS JOIN user_details B
 WHERE A.EMAIL IS NOT NULL
 AND A.FIRST_NAME IS NOT NULL
 AND A.LAST_NAME IS NOT NULL
 GROUP BY ISNULL(A.USER_NAME, B.USER_NAME), A.EMAIL, A.FIRST_NAME, A.LAST_NAME
于 2013-02-12T17:36:44.753 に答える
0

user_detailsに対して外部結合を使用してみてください。

于 2013-02-12T18:12:49.277 に答える