请帮我设计一个sql查询来解决这个问题

时间:2010-03-18 11:54:12

标签: sql mysql denormalization

alt text

对于某个特定的名字,我想获取居住在这个人居住的三个或更多城市的其他名字。

9 个答案:

答案 0 :(得分:12)

这是你应该做的:

重新设计您的数据库以拥有城市表:

city(id int, name varchar)

和用户表:

user(id int, name varchar, ...)

和user_city表:

user_city(user_id int, city_id int)

仅此一项就可以消除每个用户10个城市的限制。

找到用户居住的城市:

select city_id form user_city where user_id = ?

现在您如何在该列表中找到位于3个或更多城市的用户? 一种方法是从每个用户所在的列表中计算城市数量,例如:

select user_id,count(*) n 
from user_city 
where city_id in (select city_id 
                  from user_city 
                  where user_id = ?) 
group by user_id having n >= 3;

我没有真正测试过这个,但它应该可以工作。

你还必须弄清楚如何索引这些表。

答案 1 :(得分:4)

您需要binomial(10,3)^2 OR条件才能进行查询。多数民众赞成14 400.你不想这样做。

答案 2 :(得分:2)

您需要重新设计您的表格

name,city1,city2,city3,city4,city5,city6,city7,city8,city9 city10

应该更像是

Person, City, rank
------------------
name , city1 ,1
name , city2 ,2 
name , city3 ,3 
name , city4 ,4 
name , city5 ,5 
name , city6 ,6 
name , city7 ,7 
name , city8 ,8 
name , city9 ,9 
name , city10,10

并接受TomTom的建议并了解数据规范化!

答案 3 :(得分:2)

尊重您不重新设计数据库的请求

我没有尝试过的想法,现在没办法测试它

通过联合select name, c1select name, c2等来制作视图(姓名,城市)......

然后:

select m2.name from myview m1
inner join myview m2 on m1.city = m2.city
where m1.name = @Name AND m2.Name!=@Name
group by m2.name
having count(m2.name) > 2 

答案 4 :(得分:1)

呀。

您将表格发回给设计了它的人,并附上评论以学习设计表格。第一种正常形式,正常化。

表格遵循SQL规则后,查询非常简单。

答案 5 :(得分:1)

尝试这样的事情:

SELECT PersonName,COUNT(*) AS CountOf
    FROM (SELECT PersonName,city1 FROM PersonCities WHERE city1 IS NOT NULL
          UNION SELECT PersonName,city2 FROM PersonCities WHERE city2 IS NOT NULL
          UNION SELECT PersonName,city3 FROM PersonCities WHERE city3 IS NOT NULL
          UNION SELECT PersonName,city4 FROM PersonCities WHERE city4 IS NOT NULL
          UNION SELECT PersonName,city5 FROM PersonCities WHERE city5 IS NOT NULL
          ...
         ) dt
    WHERE dt.city1 IN (SELECT city1 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city1 IS NOT NULL
                       UNION SELECT city2 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city2 IS NOT NULL
                       UNION SELECT city3 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city3 IS NOT NULL
                       UNION SELECT city4 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city4 IS NOT NULL
                       UNION SELECT city5 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city5 IS NOT NULL
                       ...
                       )
        AND PersonName!=@SearchPerson
    GROUP BY PersonName
    HAVING COUNT(*)>=3

我没有mysql,所以这里使用SQL Server运行:

DECLARE @PersonCities table(PersonName varchar(10), city1 varchar(10), city2 varchar(10), city3 varchar(10), city4 varchar(10), city5 varchar(10))
INSERT INTO @PersonCities VALUES ('Joe','AAA','BBB','CCC', NULL, NULL)
INSERT INTO @PersonCities VALUES ('Pat','BBB','DDD','EEE','FFF','GGG')
INSERT INTO @PersonCities VALUES ('Sam','FFF','BBB', NULL, NULL, NULL)
INSERT INTO @PersonCities VALUES ('Ron','HHH','DDD','EEE','FFF', NULL)
INSERT INTO @PersonCities VALUES ('Don','FFF','ZZZ','QQQ', NULL, NULL)

DECLARE @SearchPerson varchar(10)
SET @SearchPerson='Pat'

SELECT PersonName,COUNT(*) AS CountOf
    FROM (SELECT PersonName,city1 FROM @PersonCities WHERE city1 IS NOT NULL
          UNION SELECT PersonName,city2 FROM @PersonCities WHERE city2 IS NOT NULL
          UNION SELECT PersonName,city3 FROM @PersonCities WHERE city3 IS NOT NULL
          UNION SELECT PersonName,city4 FROM @PersonCities WHERE city4 IS NOT NULL
          UNION SELECT PersonName,city5 FROM @PersonCities WHERE city5 IS NOT NULL
         ) dt
    WHERE dt.city1 IN (SELECT city1 FROM @PersonCities WHERE PersonName=@SearchPerson AND city1 IS NOT NULL
                       UNION SELECT city2 FROM @PersonCities WHERE PersonName=@SearchPerson AND city2 IS NOT NULL
                       UNION SELECT city3 FROM @PersonCities WHERE PersonName=@SearchPerson AND city3 IS NOT NULL
                       UNION SELECT city4 FROM @PersonCities WHERE PersonName=@SearchPerson AND city4 IS NOT NULL
                       UNION SELECT city5 FROM @PersonCities WHERE PersonName=@SearchPerson AND city5 IS NOT NULL
                       )
        AND PersonName!=@SearchPerson
    GROUP BY PersonName
    HAVING COUNT(*)>=3

输出:

PersonName 
---------- -----------
Ron        3

(1 row(s) affected)

答案 6 :(得分:0)

您需要normalize 您的数据库。

这样做你会得到列

名称,城市(可选择CityOrder)。

之后,您需要找到一种方法将这些结果组合成您需要的结果。这样做你需要了解加入,计数和分组。

答案 7 :(得分:0)

试试这个:

<表>人
<字段> PersonId,PersonName |

<表>市
<字段> CityId,CityName |

<表> LivedIn
<字段> LivedInId,PersonId,CityId

逻辑上,您会针对每种情况执行以下操作:

  1. 找出居住在不同城市的最大数量的人:
    列出PersonId(所有人)的列表 对此进行迭代并计算每个人居住的城市数量 找出任何人居住的最大城市 找到与具有最大城市的personId相关的人名

  2. 查找居住在3个或更多城市的所有人作为给予人 让我们给鲍勃打电话 列出鲍勃所居住的所有城市(CityIds) 制作一个包含personId和常见城市(也许是Java中的HashMap)的列表 迭代LivedIn表并更新常见的城市数量 查找所有计数大于3的人

  3. 我会用Java和SQL的组合做到这一点,但我不是那么好,所以不能在这里给你代码而不必看很多东西。

答案 8 :(得分:0)

将这些数据分成三个表格,以提供更灵活的多对多关系。

person表存储名称
用于存储城市的cityperson_city将两者(多对多)联系起来

检索居住在3个或更多城市的其他人员:

SELECT name FROM (
    SELECT
        p.name, COUNT(DISTINCT(city_id)) AS lived
    FROM person p 
    JOIN person_city pc ON (pc.person_id = p.person_id) 
    JOIN city c ON (c.city_id = pc.city_id) 
    WHERE city_id IN (
        SELECT c2.city_id 
        FROM city c2 
        JOIN person_city pc2 ON (c2.city_id = pc2.city_id) 
        JOIN person p2 ON (p2.person_id = pc2.person_id) 
        WHERE p2.name = 'navin' 
    )
    GROUP BY person_id HAVING lived >= 3
) AS multihome 
WHERE name <> 'navin';
相关问题