【问题标题】:MySQL select with joins and columns multiplication before row SUMMySQL 在行 SUM 之前使用连接和列乘法进行选择
【发布时间】:2013-06-17 05:53:38
【问题描述】:

我有很多表,我从中选择,但我只会写重要的部分。 我有 2 张桌子

住宿:

id | title | desc | etc..

住宿房间:

id | accomodation_id | beds | rooms

然后我一直选择哪个效果好,直到我想过滤,有多少床(床 * 房间)有住宿。如果我尝试使用乘法从表 accomodation_rooms 中进行简单选择,则效果很好。所以问题应该出在我加入其他表的地方。

这是我的选择:

SELECT 
  accomodation.*, 
  db_cities.title_en AS city, 
  db_cities.title_url AS city_url, 
  db_countries.title_url_en AS country_url, 
  SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons 
FROM 
  (SELECT id, aid, title_en, title_url_en, address, city_id, zip, district_id, province_id, region_id, country_id, mountain_id, stars, latitude, longitude, picture, valid_from, valid_to 
  FROM accomodation 
  ORDER BY info_date_add DESC) 
    AS accomodation 
LEFT JOIN db_cities 
  ON db_cities.id = accomodation.city_id 
JOIN db_countries 
  ON db_countries.id = accomodation.country_id 
JOIN skiresort_locations 
  ON 
    (((acos(sin((skiresort_locations.latitude*pi()/180)) * 
    sin((accomodation.latitude*pi()/180)) + 
    cos((skiresort_locations.latitude*pi()/180)) * 
    cos((accomodation.latitude*pi()/180)) * 
    cos(((skiresort_locations.longitude - 
    accomodation.longitude)*
    pi()/180))))*180/pi())*60*1.1515*1.609344) 
      < '50' 
JOIN accomodation_rooms 
  ON accomodation_rooms.accomodation_id = accomodation.id 
WHERE 
  db_countries.title_url_en LIKE '%country_title%' AND 
  accomodation.region_id = '8' 
GROUP BY 
  accomodation.aid 
HAVING 
  total_persons >= '1' 
ORDER BY CASE 
  WHEN 
    accomodation.valid_to>=NOW() AND 
    accomodation.valid_from<=NOW() 
      THEN 0 
  WHEN 
    NOW()>accomodation.valid_to AND 
    accomodation.valid_to!='0000-00-00' 
      THEN 1 
  ELSE 2 
    END, 
  accomodation.title_en 
LIMIT 10 
OFFSET 0

total_persons 应该返回 13 但它返回 624,但我真的不明白为什么?

【问题讨论】:

  • 您的一个连接表导致多行(猜测为skireshort_locations),因此您在 GROUP BY 之前的结果集上有多行(即,卧室出现多次) .因此,当您将它们相加时,它们会被计算多次(在这种情况下为 48 次)。
  • 是的,你是对的,在这种情况下,表 skiresort_locations 有 5 行。当我将 JOIN 删除到skiresort_locations 表时,total_personal 仍然不正确......我该怎么办?我应该使用一些 GROUP BY 还是为 skiresort_locations 做一些其他的 JOIN?因为有问题我只能通过距离(纬度,经度)加入skiresort_locations ...
  • 请注意,在没有任何用户变量的情况下,子查询中的 ORDER BY 子句是多余的。任何伪装都是灾难。

标签: mysql select join sum multiplication


【解决方案1】:

对新要求的进一步回复。

您需要从找到的住宿重新加入到城市表中以获取该住宿的城市详细信息:-

SELECT 
   accomodation.id, 
   accomodation.aid,
   accomodation.title_$lang, 
   accomodation.title_url_$lang,
   accomodation.address,
   accomodation.zip, 
   accomodation.stars, 
   accomodation.picture, 
   accomodation.valid_from, 
   accomodation.valid_to, 
   de_cities_accomodation.title_$lang AS city,
   de_cities_accomodation.title_url AS city_url, 
   db_countries.title_url_$lang AS country_url
FROM  db_cities 
INNER JOIN accomodation 
   ON 
   ( 
      db_cities.id = accomodation.city_id 
      OR 
      (((acos(sin((db_cities.latitude*pi()/180)) * sin((accomodation.latitude*pi()/180)) + cos((db_cities.latitude*pi()/180)) * cos((accomodation.latitude*pi()/180)) * cos(((db_cities.longitude - accomodation.longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) < '20' ) 
INNER JOIN (SELECT aid, MAX(info_date_add) AS max_info_date_add FROM accomodation GROUP BY aid) accomodation_max 
   ON 
      accomodation_max.aid = accomodation.aid AND 
      accomodation_max.max_info_date_add = accomodation.info_date_add 
LEFT JOIN db_cities de_cities_accomodation
   ON de_cities_accomodation.id = accomodation.city_id 
JOIN db_countries 
   ON db_countries.id = accomodation.country_id 
WHERE 
   db_countries.title_url_en LIKE '%country%' AND 
   db_cities.title_url LIKE '%city%' AND 
   db_cities.id = '1258' 
GROUP BY accomodation.aid 
ORDER BY 
   CASE 
      WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() THEN 0 
     WHEN NOW()>accomodation.valid_to AND accomodation.valid_to!='0000-00-00' THEN 1 
   ELSE 2 END

【讨论】:

  • 它应该有所作为,因为这座城市现在来自不同的来源。
  • 真的一样……不明白为什么
  • 恐怕我看不出有什么问题。我需要一些测试数据来重现问题。
  • 我还有一个问题要问你...我又加入了一张桌子结果就是这样,它在表 accomodation_prize 中没有任何价值。我想通过以下方式订购它: 1. 从最低价开始。 2.没有价格。我试图为 CASE 添加条件... WHEN MIN(accomodation_price.price) > '0' 但什么也没发生。我也尝试使用条件 IS NOT NULL 但也没有...
  • 我会尝试 ORDER BY WHEN accomodation_price.price IS NULL THEN 1 ELSE 0 END, accomodation_price.price 。即,按价格是否为空的标记排序,然后按其中的价格排序。
【解决方案2】:

认为您想要的数量可能最好通过使用子选择来获得。

我假设 accomodation_rooms 是一个表格,其中存储了多行,表示该位置有多少张床(即 1 x 5 床房间、2 x 3 床、5 x 2 床)。

p>
SELECT 
  accomodation.*, 
  db_cities.title_en AS city, 
  db_cities.title_url AS city_url, 
  db_countries.title_url_en AS country_url, 
  accomodation_rooms.total_persons 
FROM 
  (SELECT id, aid, title_en, title_url_en, address, city_id, zip, district_id, province_id, region_id, country_id, mountain_id, stars, latitude, longitude, picture, valid_from, valid_to 
  FROM accomodation 
  ORDER BY info_date_add DESC) 
    AS accomodation 
LEFT JOIN db_cities 
  ON db_cities.id = accomodation.city_id 
JOIN db_countries 
  ON db_countries.id = accomodation.country_id 
JOIN skiresort_locations 
  ON 
    (((acos(sin((skiresort_locations.latitude*pi()/180)) * 
    sin((accomodation.latitude*pi()/180)) + 
    cos((skiresort_locations.latitude*pi()/180)) * 
    cos((accomodation.latitude*pi()/180)) * 
    cos(((skiresort_locations.longitude - 
    accomodation.longitude)*
    pi()/180))))*180/pi())*60*1.1515*1.609344) 
      < '50' 
INNER JOIN (SELECT accomodation_id, SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons FROM accomodation_rooms GROUP BY accomodation_id) accomodation_rooms
  ON accomodation_rooms.accomodation_id = accomodation.id 
  AND accomodation_rooms.total_persons >= 1
WHERE db_countries.title_url_en LIKE '%country_title%' 
AND accomodation.region_id = '8' 
ORDER BY CASE 
  WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() 
      THEN 0 
  WHEN NOW()>accomodation.valid_to AND accomodation.valid_to!='0000-00-00' 
      THEN 1 
  ELSE 2 
    END, 
  accomodation.title_en 
LIMIT 10 
OFFSET 0

作为一个小问题,不确定您是否需要将原始选择作为子选择(或有一个 order by 子句,但您似乎没有限制,所以我认为它不会有任何效果):-

SELECT 
  accomodation.id, 
  accomodation.aid, 
  accomodation.title_en, 
  accomodation.title_url_en, 
  accomodation.address, 
  accomodation.city_id, 
  accomodation.zip, 
  accomodation.district_id, 
  accomodation.province_id, 
  accomodation.region_id, 
  accomodation.country_id, 
  accomodation.mountain_id, 
  accomodation.stars, 
  accomodation.latitude, 
  accomodation.longitude, 
  accomodation.picture, 
  accomodation.valid_from, 
  accomodation.valid_to, 
  db_cities.title_en AS city, 
  db_cities.title_url AS city_url, 
  db_countries.title_url_en AS country_url, 
  accomodation_rooms.total_persons 
FROM accomodation
LEFT JOIN db_cities 
  ON db_cities.id = accomodation.city_id 
JOIN db_countries 
  ON db_countries.id = accomodation.country_id 
JOIN skiresort_locations 
  ON 
    (((acos(sin((skiresort_locations.latitude*pi()/180)) * 
    sin((accomodation.latitude*pi()/180)) + 
    cos((skiresort_locations.latitude*pi()/180)) * 
    cos((accomodation.latitude*pi()/180)) * 
    cos(((skiresort_locations.longitude - 
    accomodation.longitude)*
    pi()/180))))*180/pi())*60*1.1515*1.609344) 
      < '50' 
INNER JOIN (SELECT accomodation_id, SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons FROM accomodation_rooms GROUP BY accomodation_id) accomodation_rooms
  ON accomodation_rooms.accomodation_id = accomodation.id 
  AND accomodation_rooms.total_persons >= 1
WHERE db_countries.title_url_en LIKE '%country_title%' 
AND accomodation.region_id = '8' 
ORDER BY CASE 
  WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() 
      THEN 0 
  WHEN NOW()>accomodation.valid_to AND accomodation.valid_to!='0000-00-00' 
      THEN 1 
  ELSE 2 
    END, 
  accomodation.title_en 
LIMIT 10 OFFSET 0

编辑 - 已修改以获取每个辅助设备的最新住宿记录,并加入该记录以获取最新住宿记录的其余部分。

SELECT 
  accomodation.*, 
  db_cities.title_en AS city, 
  db_cities.title_url AS city_url, 
  db_countries.title_url_en AS country_url, 
  accomodation_rooms.total_persons 
FROM (SELECT aid, MAX(info_date_add) AS max_info_date_add FROM accomodation GROUP BY aid) accomodation_max
INNER JOIN accomodation ON accomodation_max.aid = accomodation.aid AND accomodation_max.max_info_date_add = accomodation.info_date_add
LEFT JOIN db_cities ON db_cities.id = accomodation.city_id 
JOIN db_countries ON db_countries.id = accomodation.country_id 
JOIN skiresort_locations 
  ON 
    (((acos(sin((skiresort_locations.latitude*pi()/180)) * 
    sin((accomodation.latitude*pi()/180)) + 
    cos((skiresort_locations.latitude*pi()/180)) * 
    cos((accomodation.latitude*pi()/180)) * 
    cos(((skiresort_locations.longitude - 
    accomodation.longitude)*
    pi()/180))))*180/pi())*60*1.1515*1.609344) 
      < '50' 
INNER JOIN (SELECT accomodation_id, SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons FROM accomodation_rooms GROUP BY accomodation_id) accomodation_rooms
ON accomodation_rooms.accomodation_id = accomodation.id AND accomodation_rooms.total_persons >= 1
WHERE db_countries.title_url_en LIKE '%country_title%' 
AND accomodation.region_id = '8' 
ORDER BY CASE 
  WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() 
      THEN 0 
  WHEN NOW()>accomodation.valid_to AND accomodation.valid_to!='0000-00-00' 
      THEN 1 
  ELSE 2 
    END, 
  accomodation.title_en 
LIMIT 10 
OFFSET 0

EDIT - 使用 GROUP BY 子句为距离添加 MIN。但是不确定这会比使用 DISTINCT 快得多。它会强制对 JOIN 进行大量计算(即,如果您有 100 条住宿记录和 100 条skiresort_locations 记录,那么这将导致 10000 次合理的复杂计算来确定距离。如果您可以在计算,那么这将节省相当多的时间(例如,去规范化一点,但也许你有一个区域表连接到 region_id 上的住宿,它可能包含该区域的最小和最大纬度和经度,你可以使用对于连接,然后将复杂的计算放入 WHERE 子句中。此外,您还有一个用于 db_counties.title_url_en 的 LIKE 子句,它有一个前导通配符,这会很慢,因为它不会使用索引(尽管它应该是使用连接上的 country_id 索引)。

SELECT 
  accomodation.*, 
  db_cities.title_en AS city, 
  db_cities.title_url AS city_url, 
  db_countries.title_url_en AS country_url, 
  accomodation_rooms.total_persons,
  MIN(((acos(sin((skiresort_locations.latitude*pi()/180)) * 
    sin((accomodation.latitude*pi()/180)) + 
    cos((skiresort_locations.latitude*pi()/180)) * 
    cos((accomodation.latitude*pi()/180)) * 
    cos(((skiresort_locations.longitude - 
    accomodation.longitude)*
    pi()/180))))*180/pi())*60*1.1515*1.609344)
FROM (SELECT aid, MAX(info_date_add) AS max_info_date_add FROM accomodation GROUP BY aid) accomodation_max
INNER JOIN accomodation ON accomodation_max.aid = accomodation.aid AND accomodation_max.max_info_date_add = accomodation.info_date_add
LEFT JOIN db_cities ON db_cities.id = accomodation.city_id 
JOIN db_countries ON db_countries.id = accomodation.country_id 
JOIN skiresort_locations 
  ON 
    (((acos(sin((skiresort_locations.latitude*pi()/180)) * 
    sin((accomodation.latitude*pi()/180)) + 
    cos((skiresort_locations.latitude*pi()/180)) * 
    cos((accomodation.latitude*pi()/180)) * 
    cos(((skiresort_locations.longitude - 
    accomodation.longitude)*
    pi()/180))))*180/pi())*60*1.1515*1.609344) 
      < '50' 
INNER JOIN (SELECT accomodation_id, SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons FROM accomodation_rooms GROUP BY accomodation_id) accomodation_rooms
ON accomodation_rooms.accomodation_id = accomodation.id AND accomodation_rooms.total_persons >= 1
WHERE db_countries.title_url_en LIKE '%country_title%' 
AND accomodation.region_id = '8' 
GROUP BY accomodation.id, 
  db_cities.title_en, 
  db_cities.title_url, 
  db_countries.title_url, 
  accomodation_rooms.total_persons
ORDER BY CASE 
  WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() 
      THEN 0 
  WHEN NOW()>accomodation.valid_to AND accomodation.valid_to!='0000-00-00' 
      THEN 1 
  ELSE 2 
    END, 
  accomodation.title_en 
LIMIT 10 
OFFSET 0

【讨论】:

  • 很好用,但还有一个小问题 :) 你问我,为什么我在住宿上使用子选择。这是因为“备份” - 每次您在住宿中编辑行时,它都会插入新行。在子选择中,您可以注意到“ORDER BY info_date_add”,最后,“GROUP BY accomodation.aid”辅助是每个住宿(不是每一行)唯一的住宿标识符。我使用了你的代码,它可以工作,但只有在我编辑了住宿中的一行(现在是新行)。我注意到一件事。
  • 当我添加到首先选择“accomodation_rooms.accomodation_id AS aaa”然后我回显 aaa 时,它使用来自 accomodation_rooms 的可选 accomodation_id。我需要它将使用住宿表中最后插入的 ID。当我移除一种类型的房间 (3x2) 时,我注意到了这一点。然后它显示了 7 个可用床位,但是当我尝试找到有 10 个可用床位的住宿时,它显示了 13 个可用床位(这是仍然存储在 DB 中的旧版本)
  • 好的,会有一个小提琴。使用 GROUP BY 删除非最新住宿记录的现有方法并不安全
  • 为什么不安全?你有什么建议?
  • 我添加了一个新建议。问题是您正在通过帮助进行分组,但是它为非聚合字段从住宿中带回的列可能来自具有该帮助的任何行。例如,一张住宿记录的图片可以更改 3 次。您的 GROUP BY 将意味着它只会带回结果住宿行之一,但未定义哪一个,并且在子选择中使用排序不确定是否可以定义它。
猜你喜欢
  • 2012-06-18
  • 2013-11-18
  • 2020-09-13
  • 1970-01-01
  • 1970-01-01
  • 2020-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多