0

In the following db, I need all users and their locations, without duplicates, and with those in NJ listed first.
(If they have a NJ address, I do not need their other addresses. Database simplified for clarity).

> table address,

|  id  |  state |   City   |
|------|--------|----------|
|  01  |   NY   |  Gotham  |
|  02  |   NY   |  Uye     |
|  03  |   NJ   |  Hoboken |
|  04  |   NJ   |  Newark  |

> table contact

|  user  |  address  |
|--------|-----------|
|   01   |     01    |
|   02   |     02    |
|   02   |     03    |
|   03   |     04    |

Below are some of my attempts and their outputs. From what I have read [I have spent hours on this], I think this is called a ambiguous GROUP BY query, and is only allowed by MySQL.

I can't figure out the correct way to do this, though. Please help!

DESIRED RESULT:

|  user  |  state  |
|--------|---------|
|   02   |   NJ    |
|   03   |   NJ    |
|   01   |   NY    |

OTHER ATTEMPTS:

SELECT user, state FROM contact, address WHERE id = address;
// Duplicate users, and addresses I do not need.

|  user  |  state  |
|--------|---------|
|   01   |   NY    |
|   02   |   NY    |
|   02   |   NJ    |
|   03   |   NJ    |  

SELECT user, state FROM contact, address WHERE id = address GROUP BY user;
// NY address. I need the NJ address.

|  user  |  state  |
|--------|---------|
|   01   |   NY    |
|   02   |   NY    |
|   03   |   NJ    |

SELECT user, state FROM contact, address WHERE id = address GROUP BY user HAVING state = 'NJ';
//Worse, now I lose my NY users, and one of my NJ users doesn't even show 

|  user  |  state  |
|--------|---------|
|   03   |   NJ    |
4

3 に答える 3

3

少し遅れた答えであり、一種の長い道のりですが、標準SQLです。

SELECT DISTINCT c1.user,a1.state,a1.city
FROM contact c1
LEFT JOIN contact c2 
  ON c1.user = c2.user AND c1.address<>c2.address
JOIN address a1
  ON a1.id=c1.address
LEFT JOIN address a2 
  ON a2.id=c2.address AND
     ( a1.state<> 'NJ' AND a2.state='NJ' OR
       a1.state=a2.state AND a1.id>a2.id )
GROUP BY c1.user,a1.state,a1.city
HAVING MAX(a2.id) IS NULL

ここで SQLfiddle

EDIT:あなたが見つけた重複ケースの修正を追加しました。クエリの大まかな説明。

ユーザーごとに、連絡先/アドレス (c1/a1) のすべての組み合わせを調べ、a1/c1 が a2.id を NULL に設定する左結合を使用して、同じユーザーの他のすべての組み合わせ (a2/c2) と比較します。 "より良い"。いずれの比較でもアドレスが「劣悪」でない場合、そのアドレスは保持されます。

于 2012-10-30T17:20:31.320 に答える
3

きれいではないかもしれませんが、このソリューションは機能します。フィドルの変更を反映するために、ここでクエリを更新しました。

SELECT user, state, `state` LIKE 'NJ' ismatch
FROM contact, address WHERE id = address
AND (state = 'NJ' OR `user` NOT IN 
    (SELECT `user` FROM contact, address WHERE address = id AND `state` LIKE 'NJ'))
GROUP BY user
ORDER BY ismatch DESC;

簡単な説明: 住所が NJ にある場合はすべてのユーザーの住所に参加し、NJ に住所がない場合は任意の住所に参加します。GROUP BY は、重複したユーザーが結果に表示されないようにするために必要です。

基本的に問題は、あるユーザーに一致するアドレスが存在する場合はそのアドレスを取得し、そうでない場合はそのユーザーの任意のアドレスを取得することです。したがって、where 句の 2 番目の条件は、そのユーザーが NJ アドレスを持っている場合にのみ NJ アドレスを取得し、それ以外の場合は通常どおり最初のアドレスを取得します。ismatch フィールドはソート用です。

注: 私は元々、テーブル間の JOIN を使用してこれを書きました。これらは、わかりやすくするために削除されています。

SQL フィドルへのリンク

編集:この解決策がすべての場合に機能するとは限らないことが指摘されました(コメントを参照)。これを修正するために、GROUP BY を追加したところ、動作するようになりました。また、パフォーマンスを向上させるために CASE を単純な比較に置き換えました: SQLFiddle

さらに、データベース管理の経験がある知人から別の回答が提案されました。彼の提案は、(少なくとも) 2 つのクエリでそれを行うことだったので、それらのクエリを作成し、UNION を使用しました。

SELECT user, state, 1 ismatch FROM contact, address
WHERE id = address AND state = 'NJ' GROUP BY user
UNION
SELECT user, state, 0 ismatch FROM contact, address
WHERE id = address AND `user` NOT IN (SELECT `user` FROM contact, address WHERE address = id AND `state` LIKE 'NJ')
GROUP BY user ORDER BY ismatch DESC

簡単な説明: NJ の住所を持つすべてのユーザーを取得し、NJ の住所がない場合は任意の住所を持つすべてのユーザーを取得し、2 つの結果セットを UNION します。GROUP BY は、重複を防ぐために両方のクエリで使用されます。

于 2012-10-30T12:52:55.210 に答える
1

これを試して:

SELECT user, state FROM contact, address WHERE id = address 
and state='NJ'
union
SELECT min(user), state FROM contact, address WHERE id = address 
and state<>'NJ'
group by state

SQLFIDDLE デモ

于 2012-10-30T12:29:53.883 に答える