0

在下面的数据库中,我需要所有用户及其位置,没有重复,并且首先列出了新泽西州的用户。
(如果他们有新泽西地址,我不需要他们的其他地址。为清楚起见,简化了数据库)。

> 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    |

以下是我的一些尝试及其输出。从我读到的[我在这上面花了几个小时],我认为这被称为模棱两可的 GROUP BY 查询,只有 MySQL 才允许。

不过,我想不出正确的方法来做到这一点。请帮忙!

期望的结果:

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

其他尝试:

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

它可能不漂亮,但这个解决方案有效。我在这里更新了查询以反映小提琴中的变化:

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;

简单解释:将每个用户加入他在新泽西州的地址,或者如果他在新泽西州没有地址,则加入他的任何地址。需要 GROUP BY 以防止重复用户出现在结果中。

本质上,问题归结为想要检索用户的匹配地址(如果存在),否则只是获取该用户的任何地址。因此 where 子句中的第二个条件,只有当该用户有一个 NJ 地址时才会提取一个 NJ 地址,否则它将正常取第一个。ismatch 字段用于排序。

注意:我最初是使用表之间的 JOIN 编写的。为了清楚起见,它们已被删除。

链接到 SQL 小提琴

编辑:有人向我指出,此解决方案并非在所有情况下都有效(请参阅评论)。为了解决这个问题,我添加了一个 GROUP BY,现在它似乎可以工作了。我还用一个简单的比较替换了 CASE 以提高性能:SQLFiddle

此外,我认识的具有数据库管理经验的人向我建议了一个替代答案。他的建议是(至少)在两个查询中执行此操作,因此我构建了这些查询并使用了 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 地址,并将两个结果集合并在一起。GROUP BY 用于两个查询以防止重复。

于 2012-10-30T12:52:55.210 回答
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: Added a fix for the duplicate case you found. A cursory explanation of the query;

For each user, it will go through all combinations of contact/address (c1/a1) and compare them with all other (a2/c2) for the same user using a left join that sets a2.id to NULL if a1/c1 is "better". If an address isn't "worse" in any comparison, it will be the address kept.

于 2012-10-30T17:20:31.320 回答
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 回答