【问题标题】:SQL select entries in other table linked by foreign keysSQL 选择外键链接的其他表中的条目
【发布时间】:2013-09-21 20:54:58
【问题描述】:

我已经重新设计了我的数据库结构,以使用主键和外键将我的 3 个表中的条目链接在一起,但在给定另一个表中的数据的情况下,我在尝试编写查询以选择一个表中的数据时遇到问题。这是我的 3 个 CREATE TABLE 语句的示例:

CREATE TABLE IF NOT EXISTS players (
    id INT(10) NOT NULL AUTO_INCREMENT,
    username VARCHAR(16) NOT NULL, 
    uuid VARCHAR(200) NOT NULL DEFAULT 0,
    joined TIMESTAMP DEFAULT 0,
    last_seen TIMESTAMP DEFAULT 0,
    PRIMARY KEY (id)
);

/*      ^
    One |
       To
        | One
        v
*/

CREATE TABLE IF NOT EXISTS accounts (
    id INT(10) NOT NULL AUTO_INCREMENT,
    account_id INT(10) NOT NULL,
    pass_hash VARCHAR(200) NOT NULL, 
    pass_salt VARCHAR(200) NOT NULL, 
    created BIGINT DEFAULT 0,
    last_log_on BIGINT DEFAULT 0,
    PRIMARY KEY (id),
    FOREIGN KEY (account_id) REFERENCES players(id) ON DELETE CASCADE
) ENGINE=InnoDB;

/*      ^
    One |
       To
        | Many
        v
*/

CREATE TABLE IF NOT EXISTS purchases (
    id INT(10) NOT NULL AUTO_INCREMENT,
    account_id INT(10) NOT NULL,
    status VARCHAR(20) NOT NULL, 
    item INT NOT NULL, 
    price DOUBLE DEFAULT 0,
    description VARCHAR(200) NOT NULL,
    buyer_name VARCHAR(200) NOT NULL,
    buyer_email VARCHAR(200) NOT NULL,
    transaction_id VARCHAR(200) NOT NULL,
    payment_type VARCHAR(20) NOT NULL,
    PRIMARY KEY (id),
    FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE
) ENGINE=InnoDB;

例如,我想选择购买超过 30 美元商品的用户的所有用户名。所有用户名都存储在 player 表中,该表链接到 accounts 表并链接到 purchase 表。这是设计这个关系数据库的最佳方式吗?如果是这样,我将如何运行类似于上述示例的查询?

我能够根据用户名获取所有用户的购买历史记录,但我使用 2 个子查询来完成此操作...获取该数据应该比这更容易! 这是我为获取所有玩家购买数据而运行的 SELECT 查询:

SELECT * 
FROM purchases
WHERE account_id = (SELECT id FROM accounts WHERE account_id = (SELECT id FROM players WHERE username = 'username'));

此外,当我尝试使用“players.username”之类的内容引用其他表时,我收到一条错误消息,指出该列不存在...

感谢您的帮助!谢谢!

【问题讨论】:

  • @Sam 哇,我应该知道加入与此有关!我应该做更多的研究!感谢您的链接!我能够使用内部连接查询我在问题中提供的示例: SELECT username FROM player INNER JOIN accounts ON player.id= accounts.account_id INNER JOIN purchase ON accounts.id = purchase.account_id WHERE purchase.price >= 30;
  • 好吧,每个人都开始一次:)。该查询本身看起来是正确的,但是,您只是加入 一次 购买,因此您可能会错过价格 > 30 的购买(我假设是一对多的关系)。这些购买的子选择可以做到这一点,或者,您可以使用右外连接和不同的,或者简单地将price > 30 条件放入购买的连接谓词中

标签: mysql sql database


【解决方案1】:

我认为您的设计还可以。玩家和帐户之间的关系是一对多而不是一对一,因为这样,您可以有两个元组引用单个玩家。

我会把你需要的查询写成:

SELECT DISTINCT p.id, p.username
FROM players p INNER JOIN accounts a ON (p.id = a.account_id)
               INNER JOIN purchases pc ON (a.id = pc.account_id)
WHERE (pc.price > 30);

正如 Sam 所建议的,我添加了 DISTINCT 以避免重复 id 和 username 以防用户多次购买。 请注意这里的 id 是为了避免重复用户名之间的混淆。

【讨论】:

  • 这取决于你。基本上,最好有一个实际的答案来接受。但是,如果玩家有多个购买或帐户,则该查询不会返回正确的结果。
  • 活泼,正如黄鼠狼 xD 所期望的那样。我不是来偷答案的,试试这个:SELECT p.username FROM players p INNER JOIN accounts a ON (p.id = a.account_id) INNER JOIN purchases pc ON (a.id = pc.account_id and pc.price > 30) ; ...我们在这里谈论金钱:)
  • 哦,这比我的解决方案执行得更快。我不会想到排除 Where 而是在第二个连接中过滤结果。我想知道这是否是导致执行时间减少的原因,或者是否是别名的原因。别名是否也仅用于使查询文本更短?
  • 是的,理论上(独立于 SQL 供应商)在连接时进行过滤会更快,因为 WHERE 子句在连接完成后进行过滤,您将获得“临时”表。 Sam 的解决方案更快,因为它在加入过程中应用了过滤器。但是今天的 sql 引擎足够聪明,可以检测到这种情况并对其进行优化。见stackoverflow.com/questions/10297231/…
猜你喜欢
  • 2023-03-10
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 2019-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多