【问题标题】:Select rows from table where id is in a comma seperated list in another table从表中选择行,其中 id 在另一个表的逗号分隔列表中
【发布时间】:2011-07-29 09:29:52
【问题描述】:

我有这两个查询,我想一起加入, 但不够熟练。

谁能帮帮我?

这是选择与搜索匹配的行 其中包含一个 cids 列,而我需要在第二个查询中使用它

SELECT cids,tp FROM searches_done where (keyword='keyword' and city='city' and page='page' and ipp='ipp') LIMIT 1

此查询选择实际结果行,将每个公司的 id 与我们在第一个查询中获得的列分隔值 (cids) 进行比较 此外,它需要将第一个查询中的列 tp 附加到每个结果

SELECT c.*,s.tp FROM companies c WHERE c.id in (cids)

目前,由于我运行了两个查询(通过代码),所以它可以工作, 并在第二个查询中使用我从第一个查询中获得的结果。

searches_done table structure:

id - autoincrement unique int  
keyword - varchar(255) - keyword that was searched
page - int - paging page ( i.e: page=0 its the first page etc.. )
city - varchar(255) - keyword for city that was searched 
cids - text - comma seperated values of the companies ids 
                ( that were found matching this search )   
tp - int - total pages found
ipp - int - itemsperpage ( a search could be done with only 10 items per page
                         , so diffrent results might be found with 10/100 )

我现在正在创建一个包含sd_id, c_id 的新表以规范化数据库:\

【问题讨论】:

  • 那么我应该如何保存它们呢?为这些值创建另一个表似乎很奇怪。还是我错了?
  • 我们需要更多关于表结构的信息,但是这通常是解决方案。
  • 没有了。。我的表中的所有字段都如图所示。 Companies 表包含更多字段(公司数据,如电话等),但与其他表没有任何连接(除了 ID 之外,如上所示,它也写在 search_done.cids 中)
  • 好的,您可以编辑问题来描述您的 searchs_done 表吗?
  • 好的,现在我有一个名为 cids 的表,其中包含 sd_id、c_id。好点了吗?

标签: mysql join where


【解决方案1】:

可以使用函数FIND_IN_SET()

SELECT ...
FROM companies c
  JOIN searches_done sd
    ON FIND_IN_SET( c.id, sd.cids) > 0
WHERE sd.keyword = 'keyword' 
  AND sd.city = 'city'
  AND sd.page = 'page'
  AND sd.ipp = 'ipp'

但是你真的应该规范化表格。不使用逗号分隔列表有几个好处。


如何将数据传输到规范化关联表中:

--- create table
CREATE TABLE search_companies
( sd_id INT NOT NULL
, c_id INT NOT NULL
, PRIMARY KEY (sd_id, c_id)
, FOREIGN KEY sd_id REFERENCES searches_done(id)    --- if you use InnoDB
, FOREIGN KEY c_id REFERENCES companies(id)         --- 
) ;

--- transfer data
INSERT INTO search_companies
    (sd_id, c_id)
SELECT DISTINCT
    sd.id, c.id
FROM companies c
  JOIN searches_done sd
    ON FIND_IN_SET( c.id, sd.cids) > 0

那么您的查询将使用简单的 JOIN:

SELECT ...
FROM companies c
  JOIN searches_companies sc
    ON sc.c_id = c.id
  JOIN searches_done sd
    ON sd.id = sc.sd_id 
WHERE sd.keyword = 'keyword' 
  AND sd.city = 'city'
  AND sd.page = 'page'
  AND sd.ipp = 'ipp'

在检查一切正常且所有查询(SELECT、INSERT、DELETE、UPDATE)运行正常后,您可以删除 cids 字段。


这里有一个很好的答案,关于这个问题:is storing a comma separated list in a database column really that bad?

【讨论】:

  • 你似乎总是想出一个解决办法! ;) +1
  • 问题:JOINFIND_IN_SET' is faster or prefered over WHERE c.id in (SELECT cids FROM searched_done) 吗?
  • @Dementic:JOIN 通常是最快的选择。 WHERE c.id IN (2,5,17,42,...) 通常也很快。 (两者都有适当的索引)。 FIND_IN_SET() 是最慢的,因为必须扫描整个表(在这种情况下,c.id 来自表 companies),不能使用索引。
  • 逗号分隔列表的问题在于(尽管WHERE c.id IN (2,5,17,42,...) 可以使用并且速度很快),您必须为一个特定的search_done 记录运行两个查询。更重要的是,您不能强制执行参照完整性(例如,当公司被删除时如何从所有列表中删除 c.id?,如何检查列表中没有添加两次 c.id?),您可以' t 轻松(或快速)计算(逗号分隔)列表中存在多少项目或进行其他聚合计算。
  • @Dementic:检查我在答案末尾添加的链接。
【解决方案2】:

您几乎可以让 searchs_done 表保持原样:

searches_done 表结构:

id - autoincrement unique int
keyword - varchar(255) 
page - int 
city - varchar(255) 
tp - int 
ipp - int 

然后添加另一个表:

搜索公司:

search_id int foreign key searches_done.id
company_id int foreign key company.id

然后您可以连接这两个表以获得结果:

SELECT c.id, tp 
FROM companies c
INNER JOIN searches_company sc ON c.id = sc.company_id
INNER JOIN searches_done sd ON sc.search_id = sd.id
WHERE ...

【讨论】:

  • 谢谢 cularis,这很有帮助!
猜你喜欢
  • 1970-01-01
  • 2012-11-03
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
相关资源
最近更新 更多