【问题标题】:MySQL using IN/FIND_IN_SET to read multiple rows in sub queryMySQL 使用 IN/FIND_IN_SET 读取子查询中的多行
【发布时间】:2012-01-17 18:51:28
【问题描述】:

我有两个表,位置和位置组

CREATE TABLE locations (
    location_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(63) UNIQUE NOT NULL
);

INSERT INTO locations (name)
  VALUES
('london'),
('bristol'),
('exeter');

CREATE TABLE location_groups (
    location_group_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    location_ids VARCHAR(255) NOT NULL,
    user_ids VARCHAR(255) NOT NULL,
    name VARCHAR(63) NOT NULL,
);

INSERT INTO location_groups (location_ids, user_ids, name)
  VALUES
('1', '1,2,4', 'south east'),
('2,3', '2', 'south west');

我要做的是返回给定 user_id 存在的所有 location_groups 的所有 location_ids。我正在使用 CSV 将 location_ids 和 user_ids 存储在 location_groups 表中。我知道这不是标准化的,但数据库就是这样,它超出了我的控制范围。

我目前的查询是:

SELECT location_id 
  FROM locations 
  WHERE FIND_IN_SET(location_id, 
      (SELECT location_ids 
         FROM location_groups 
         WHERE FIND_IN_SET(2,location_groups.user_ids)) )

现在,如果 user_id = 1(例如仅返回 1 个 location_group 行),这可以正常工作,但是如果我搜索 user_id = 2,我会收到错误消息,指出子查询返回超过 1 行,这是预期的因为用户 2 在 2 个 location_groups 中。我明白为什么会抛出错误,我正在尝试解决它。​​

为了澄清在 location_groups.user_ids 中搜索 user_id 1 时,应返回 location_id 1。搜索 user_id 2 时,应返回 location_ids 1、2、3。

我知道这是一个复杂的查询,所以如果有任何不清楚的地方,请告诉我。任何帮助,将不胜感激!谢谢。

【问题讨论】:

  • 不要放弃!为正常化而战!
  • 正确的sample code(此处为 SQL 语句)比任何即席模式和示例数据格式都更有用。样品请使用CREATE TABLEINSERT ... VALUES。所需的结果不需要以示例代码的形式呈现,因为结果是代码的输出,而不是代码本身。

标签: mysql subquery in-subquery


【解决方案1】:

您可以在子查询中使用GROUP_CONCAT 组合location_ids。

SELECT location_id 
FROM locations 
WHERE FIND_IN_SET(location_id, 
    (SELECT GROUP_CONCAT(location_ids)
     FROM location_groups 
     WHERE FIND_IN_SET(2,location_groups.user_ids)) )

或者,使用编写查询的问题作为规范化为何好的示例。哎呀,即使您确实使用了这个查询,它也会比在正确规范化的表上的查询运行得更慢;你可以用它来说明为什么应该重组表格。


作为参考(以及其他读者),以下是规范化模式的外观(包括对基表的一些额外更改)。

location_groups 表中的复合字段可以简单地分成额外的行来实现1NF,但这不会在2NF 中,因为name 列将仅依赖于@987654329 @(location, user) 候选键的一部分。 (另一种思考方式是name 是区域的属性,而不是区域/组、位置和用户之间的关系。)

相反,这些列将被拆分为 1NF 的两个附加表:一个用于连接位置和区域,另一个用于连接用户和区域。后者可能应该是用户和位置(而不是区域)之间的关系,但当前模式并非如此(这可能是当前非规范化模式的另一个问题)。区域-位置关系是一对多的(因为每个位置都在一个区域中)。从样本数据中,我们看到区域-用户关系是多对多的。然后location_groups 表变为region 表。

-- normalized from `location_groups`
CREATE TABLE regions (
    `id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(63) UNIQUE NOT NULL
);

-- slightly altered from original
CREATE TABLE locations (
    `id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(63) UNIQUE NOT NULL
);

-- missing from original sample
CREATE TABLE users (
    `id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(63) UNIQUE NOT NULL
);

-- normalized from `location_groups`
CREATE TABLE location_regions (
    `region` INT UNSIGNED,
    `location` INT UNSIGNED UNIQUE NOT NULL,
    PRIMARY KEY (`region`, `location`),
    FOREIGN KEY (`region`)
        REFERENCES regions (id)
        ON DELETE restrict ON UPDATE cascade,
    FOREIGN KEY (`location`)
        REFERENCES locations (id)
        ON DELETE cascade ON UPDATE cascade
);

-- normalized from `location_groups`
CREATE TABLE user_regions (
    `region` INT UNSIGNED NOT NULL,
    `user` INT UNSIGNED NOT NULL,
    PRIMARY KEY (`region`, `user`),
    FOREIGN KEY (`region`)
        REFERENCES regions (id)
        ON DELETE restrict ON UPDATE cascade,
    FOREIGN KEY (`user`)
        REFERENCES users (id)
        ON DELETE cascade ON UPDATE cascade
);

样本数据:

INSERT INTO regions
  VALUES
('South East'),
('South West'),
('North East'),
('North West');

INSERT INTO locations (`name`)
  VALUES
('London'),
('Bristol'),
('Exeter'),
('Hull');

INSERT INTO users (`name`)
  VALUES
('Alice'),
('Bob'),
('Carol'),
('Dave'),
('Eve');

------ Location-Region relation ------
-- temporary table used to map natural keys to surrogate keys
CREATE TEMPORARY TABLE loc_rgns (
    `location` VARCHAR(63) UNIQUE NOT NULL
    `region` VARCHAR(63) NOT NULL,
);

-- Hull added to demonstrate correctness of desired query
INSERT INTO loc_rgns (region, location)
  VALUES
('South East', 'London'),
('South West', 'Bristol'),
('South West', 'Exeter'),
('North East', 'Hull');

-- map natural keys to surrogate keys for final relationship
INSERT INTO location_regions (`location`, `region`)
  SELECT loc.id, rgn.id
    FROM locations AS loc
      JOIN loc_rgns AS lr ON loc.name = lr.location
      JOIN regions AS rgn ON rgn.name = lr.region;

------ User-Region relation ------
-- temporary table used to map natural keys to surrogate keys
CREATE TEMPORARY TABLE usr_rgns (
    `user` INT UNSIGNED NOT NULL,
    `region` VARCHAR(63) NOT NULL,
    UNIQUE (`user`, `region`)
);

-- user 3 added in order to demonstrate correctness of desired query
INSERT INTO usr_rgns (`user`, `region`)
  VALUES
(1, 'South East'),
(2, 'South East'),
(2, 'South West'),
(3, 'North West'),
(4, 'South East');

-- map natural keys to surrogate keys for final relationship
INSERT INTO user_regions (`user`, `region`)
  SELECT user, rgn.id
    FROM usr_rgns AS ur
      JOIN regions AS rgn ON rgn.name = ur.region;

现在,规范化模式的所需查询:

SELECT DISTINCT loc.id
FROM locations AS loc
  JOIN location_regions AS lr ON loc.id = lr.location
  JOIN user_regions AS ur ON lr.region = ur.region
;

结果:

+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+

【讨论】:

  • 完美,正是我需要的!谢谢。
猜你喜欢
  • 1970-01-01
  • 2014-08-15
  • 2013-07-12
  • 1970-01-01
  • 2011-10-27
  • 2013-09-18
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
相关资源
最近更新 更多