4张表进行INNER和LEFT JOIN是否正常?

Is it normal to perform INNER and LEFT JOIN with 4 tables?

我正在建立一个社区网站,我做了一个时间线系统(就像 Facebook)。人们可以 post 发短信,或创建和 post 事件作为 post(post type 将是 'event')。

我有以下 tables:

帖子

id(pk)user_idcontenttypespacespace_iddatevisibility

用户

id(pk)usernameprofile_photof_namel_name

id(pk)post_iduser_id

事件

id(pk)titlevisibilityevent_type

我注意到获取 5 个 post 需要 700 毫秒,因为我在一个查询中获取了 post,然后我对每个 post 执行所有其他查询在 while-fetch 循环中(php)。 (很多 SQL 调用)

我想获取 posts、post 所有者(用户)、post 点赞数和 post 事件 if post type 是事件,这就是我想出的:

SELECT COUNT(l.id) as likes_count,
p.id, p.user_id, p.content, p.type, p.space, p.space_id, p.date, p.visibility,
e.id as event_id, e.title as event_title, e.visibility as event_visibility, e.event_type,
u.username, u.id as user_id, u.profile_photo, u.f_name, u.l_name
FROM `posts` p

INNER JOIN `users` u
ON u.id = p.user_id
AND p.space = :space
AND p.space_id = :space_id


LEFT JOIN `events` e
ON p.id = e.parent_id
AND e.parent = :space
AND e.space_id = :space_id
        

LEFT JOIN `likes` l
ON p.id = l.post_id
GROUP BY p.id
LIMIT 0, 5

这个查询工作正常(我认为),但它似乎效率低下并且 hacky。我不是 SQL 专家,这是获得我需要的正常且良好的查询吗?或者还有其他方法吗?加入 4 tables 并在一次查询中获取所有这些信息是否合乎逻辑? (想象一下 posts table 中的 1 亿 posts)

这些是具有虚拟内容的 table:

CREATE TABLE `posts` (
  `id` int(11) NOT NULL,
  `space` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `space_id` int(11) NOT NULL,
  `type` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
  `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `visibility` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
  `date` timestamp NOT NULL DEFAULT current_timestamp(),
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE `likes` (
  `id` int(11) NOT NULL,
  `space` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `space_id` int(11) NOT NULL,
  `post_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `date` timestamp NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `uuid` binary(16) NOT NULL,
  `f_name` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
  `l_name` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
  `email` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
  `email_visible` tinyint(1) NOT NULL DEFAULT 0,
  `phone_number` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `phone_number_visible` tinyint(1) NOT NULL DEFAULT 0,
  `username` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
  `password` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `login_token` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
  `birthday` date NOT NULL,
  `city` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `country` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `state_province` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `goals` longtext COLLATE utf8_unicode_ci DEFAULT NULL,
  `motivations` longtext COLLATE utf8_unicode_ci DEFAULT NULL,
  `cover_photo` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `profile_photo` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `gender` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `v_token` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
  `ride_giver` tinyint(1) NOT NULL DEFAULT 0,
  `advisor` tinyint(1) NOT NULL DEFAULT 0,
  `verified` tinyint(1) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE `events` (
  `id` int(11) NOT NULL,
  `title` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
  `start_date` date NOT NULL,
  `end_date` date NOT NULL,
  `start_time` time NOT NULL,
  `end_time` time NOT NULL,
  `location` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
  `description` longtext COLLATE utf8_unicode_ci NOT NULL,
  `event_type` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
  `visibility` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
  `parent` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `parent_id` int(11) NOT NULL,
  `space_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


INSERT INTO `posts` (`id`, `space`, `space_id`, `type`, `content`, `visibility`, `date`, `user_id`) VALUES
(2, 'sharing_circle', 3, 'text', '1st post on 1st sharing circle!', 'public', '2021-02-24 13:10:17', 1),
(254, 'sharing_circle', 3, 'document', 'Testing file upload... ', 'public', '2021-03-04 20:30:47', 1),
(255, 'sharing_circle', 3, 'image', 'Testing single image upload...', 'public', '2021-03-04 20:32:01', 1),
(256, 'sharing_circle', 3, 'image', 'Testing multiple image upload...', 'public', '2021-03-04 20:32:27', 1),
(257, 'sharing_circle', 3, 'event', '', 'public', '2021-03-04 20:34:05', 1),
(259, 'sharing_circle', 3, 'text', 'Private post...', 'private', '2021-03-04 20:34:51', 1),
(263, 'sharing_circle', 3, 'text', 'THIS IS AWESOME!', 'public', '2021-03-05 13:46:25', 3),
(264, 'sharing_circle', 3, 'text', 'Looking good!', 'public', '2021-03-05 15:31:35', 4),
(268, 'sharing_circle', 3, 'text', 'Pretty cool space :D', 'public', '2021-03-08 14:43:18', 5),
(269, 'sharing_circle', 3, 'text', 'Hello', 'public', '2021-03-08 14:44:05', 3),
(270, 'sharing_circle', 4, 'text', 'First Razzaky post', 'public', '2021-03-08 15:29:24', 3),
(432, 'sharing_circle', 3, 'text', 'Testing FCM!', 'public', '2021-03-19 17:01:30', 1);


INSERT INTO `likes` (`id`, `space`, `space_id`, `post_id`, `user_id`, `date`) VALUES
(40, 'sharing_circle', 3, 254, 1, '2021-03-04 20:30:54'),
(41, 'sharing_circle', 3, 257, 1, '2021-03-04 20:34:12'),
(42, 'sharing_circle', 3, 259, 1, '2021-03-04 23:38:10'),
(43, 'sharing_circle', 3, 257, 3, '2021-03-05 13:43:08'),
(44, 'sharing_circle', 3, 256, 3, '2021-03-05 13:43:16'),
(45, 'sharing_circle', 3, 255, 3, '2021-03-05 13:43:24'),
(46, 'sharing_circle', 3, 2, 3, '2021-03-05 13:45:47'),
(47, 'sharing_circle', 3, 263, 3, '2021-03-05 13:46:29'),
(48, 'sharing_circle', 3, 264, 1, '2021-03-05 19:33:16'),
(51, 'sharing_circle', 3, 263, 1, '2021-03-05 19:37:39'),
(52, 'sharing_circle', 3, 269, 3, '2021-03-08 14:44:21'),
(53, 'sharing_circle', 3, 264, 3, '2021-03-08 14:46:57'),
(54, 'sharing_circle', 4, 270, 3, '2021-03-08 15:29:27'),
(56, 'sharing_circle', 3, 268, 1, '2021-03-11 13:30:30'),
(57, 'sharing_circle', 4, 364, 1, '2021-03-11 22:25:20'),
(60, 'sharing_circle', 3, 352, 1, '2021-03-13 16:55:37'),
(61, 'sharing_circle', 4, 365, 1, '2021-03-14 18:15:23'),
(84, 'advisory_pod', 1, 392, 1, '2021-03-23 11:34:13'),
(85, 'advisory_pod', 1, 392, 2, '2021-03-23 11:34:23'),
(86, 'sharing_circle', 3, 432, 2, '2021-03-23 11:35:08'),
(87, 'sharing_circle', 3, 352, 2, '2021-03-23 11:36:18'),
(88, 'sharing_circle', 3, 259, 2, '2021-03-23 11:36:22'),
(89, 'sharing_circle', 3, 2, 2, '2021-03-23 13:52:42'),
(90, 'sharing_circle', 3, 432, 1, '2021-03-23 19:27:04'),
(92, 'sharing_circle', 4, 270, 1, '2021-03-26 14:05:18'),
(93, 'advisory_pod', 1, 392, 3, '2021-03-26 15:37:26');



INSERT INTO `events` (`id`, `title`, `start_date`, `end_date`, `start_time`, `end_time`, `location`, `description`, `event_type`, `visibility`, `parent`, `parent_id`, `space_id`, `user_id`) VALUES
(33, 'First Sharing Circles First Event! Yay!', '2021-03-26', '2021-03-27', '12:00:00', '14:00:00', 'TBD', 'This is the first sharing circle first event.<br>This is the first event ever to be created on this platform!<br>Nice stuff!', 'event', 'public', 'sharing_circle', 257, 3, 1),

INSERT INTO `users` (`id`, `uuid`, `f_name`, `l_name`, `email`, `email_visible`, `phone_number`, `phone_number_visible`, `username`, `password`, `login_token`, `birthday`, `city`, `country`, `state_province`, `goals`, `motivations`, `cover_photo`, `profile_photo`, `gender`, `v_token`, `ride_giver`, `advisor`, `verified`) VALUES
(1, '', 'Fadi', 'Obaji', 'dfg', 0, 'fdg', 0, 'fadiobaji', '', '', '1986-09-07', 'country', 'city', 'BAŞAKŞEHİR', 'Later', 'BRB', 'fv5060413b667ca8f7.07910603ccz', 'an2560413b6b808333.46515380olg', 'male', '', 1, 1, 1),
(2, '', 'Fadi', 'Obaji (Second Account)', 'dfg', 0, '', 0, 'fadi.obaji', '', '', '1986-09-07', 'country', 'city', 'Başakşehir', '', '', 'jk1460537093416c74.26410577ewv', 'ha206050f3ab75ac51.60907207pxr', 'male', '', 1, 1, 1),

因为在你的 group by 子句中你只有 p.id 这个查询不应该 运行 因为你的 select 列表中有一些列既不在 group by 子句中也没有任何聚合。

我检查了下面的查询,select输入的数据是正确的。请检查 dbfiddle link.

查询:

 SELECT l.like_count as likes_count,
 p.id, p.user_id, p.content, p.type, p.space, p.space_id, p.date, p.visibility,
 e.id as event_id, e.title as event_title, e.visibility as event_visibility, e.event_type,
 u.username, u.id as user_id, u.profile_photo, u.f_name, u.l_name
 FROM `posts` p
 
 INNER JOIN `users` u
 ON u.id = p.user_id
 
 LEFT JOIN events e
 ON p.id = e.parent_id       
 
 LEFT JOIN (select post_id,count(*)like_count from likes group by post_id) l
 ON p.id = l.post_id

输出:

 likes_count |  id | user_id | content                                                          | type     | space          | space_id | date                | visibility | event_id | event_title                             | event_visibility | event_type | username  | user_id | profile_photo                  | f_name | l_name
 ----------: | --: | ------: | :--------------------------------------------------------------- | :------- | :------------- | -------: | :------------------ | :--------- | -------: | :-------------------------------------- | :--------------- | :--------- | :-------- | ------: | :----------------------------- | :----- | :-----
           2 |   2 |       1 | 31737420706f7374206f6e203173742073686172696e6720636972636c6521   | text     | sharing_circle |        3 | 2021-02-24 13:10:17 | public     |     <em>null</em> | <em>null</em>                                    | <em>null</em>             | <em>null</em>       | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 
           1 | 254 |       1 | 54657374696e672066696c652075706c6f61642e2e2e20                   | document | sharing_circle |        3 | 2021-03-04 20:30:47 | public     |     <em>null</em> | <em>null</em>                                    | <em>null</em>             | <em>null</em>       | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 
           1 | 255 |       1 | 54657374696e672073696e676c6520696d6167652075706c6f61642e2e2e     | image    | sharing_circle |        3 | 2021-03-04 20:32:01 | public     |     <em>null</em> | <em>null</em>                                    | <em>null</em>             | <em>null</em>       | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 
           1 | 256 |       1 | 54657374696e67206d756c7469706c6520696d6167652075706c6f61642e2e2e | image    | sharing_circle |        3 | 2021-03-04 20:32:27 | public     |     <em>null</em> | <em>null</em>                                    | <em>null</em>             | <em>null</em>       | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 
           2 | 257 |       1 |                                                                  | event    | sharing_circle |        3 | 2021-03-04 20:34:05 | public     |       33 | First Sharing Circles First Event! Yay! | public           | event      | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 
           2 | 259 |       1 | 5072697661746520706f73742e2e2e                                   | text     | sharing_circle |        3 | 2021-03-04 20:34:51 | private    |     <em>null</em> | <em>null</em>                                    | <em>null</em>             | <em>null</em>       | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 
           2 | 432 |       1 | 54657374696e672046434d21                                         | text     | sharing_circle |        3 | 2021-03-19 17:01:30 | public     |     <em>null</em> | <em>null</em>                                    | <em>null</em>             | <em>null</em>       | fadiobaji |       1 | an2560413b6b808333.46515380olg | Fadi   | Obaji 

db<>fiddle here

您的查询基本没问题。但是,您在 ON 子句中过滤内部联接。更典型的是,这是在 WHERE 子句中完成的。

并且因为您正在过滤,所以我会建议对计数进行相关子查询:

SELECT (SELECT COUNT(*)
        FROM likes l
        WHERE p.id = l.post_id
       ) as likes_count
       p.id, p.user_id, p.content, p.type, p.space, p.space_id, p.date, p.visibility,
       e.id as event_id, e.title as event_title, e.visibility as event_visibility, e.event_type,
       u.username, u.id as user_id, u.profile_photo, u.f_name, u.l_name
FROM `posts` p INNER JOIN 
     `users` u
     ON u.id = p.user_id  LEFT JOIN
     `events` e AND
     ON p.id = e.parent_id
        e.parent = p.space AND
        e.space_id = p.space_id
WHERE p.space = :space AND p.space_id = :space_id
ORDER BY p.id
LIMIT 0, 5;

如果你有合理的数据量和明显的索引,你应该会发现这个版本要快得多。