寻找更好的方法从php中的多个查询的sql表中选择

时间:2017-09-19 10:45:24

标签: php mysql sql

我有三张桌子:"用户","帖子","喜欢"几乎格式化为: 例如,三个表条目是:

users (two users): 1. uid: 12,
                   2. uid: 15.

posts (three posts): 1. pid: 3, publisherId = 12, likers = 2,
                     2. pid: 6, publisherId = 12, likers = 0,
                     3. pid: 7, publisherId = 12, likers = 1.

likes (three likes): 1. lid: 1, postId = 3, likerId = 12,
                     2. lid: 2, postId = 7, likerId = 15,
                     3. lid: 3, postId = 3, likerId = 15.

我需要的是:获取多维数组中的所有帖子,其中包含用于唯一发布者(用户)的数组和用于likers的另一个数组(用户也)。我正在寻找的输出是这样的:

Array: (
        post:(
              pid = 3,
              publisher = Array (uid = 12),
              likers = Array (uid=12, uid=15)
        ),
        post:( ....
        )
 ).

我已经在以下时间消耗了(我相信):

$sql = "SELECT dev_posts.* FROM posts";

if (!$result = mysql_query($sql)) die("Query failed.");

$response = array();
while($result_array = mysql_fetch_object($result)) {
    $entries = array();
    foreach($result_array as $key => $value) {
        if ($key == "byUserId") {
            $publisherID = $result_array->byUserId;
            $anotherSql = "SELECT * FROM users WHERE users.uid = $publisherID";
            if ($anotherResult = mysql_query($anotherSql)) {
                $anothers = array();
                while($anotherResult_array = mysql_fetch_object($anotherResult)) {
                    $another = array();
                    foreach($anotherResult_array as $anotherKey => $anotherValue) {
                        $another[$anotherKey] = $anotherValue;
                    }
                    $anothers[] = $another;
                }
                $entries[$key] = $anothers;
            }
        }
        else if ($key == "likes") {
            if ($value > 0){
                $PID = $result_array->pid;
                $anotherSql = "SELECT likes.*, users.* FROM likes LEFT JOIN users ON likes.likeUserId = users.uid WHERE $PID = likes.likePostId";
                if ($anotherResult = mysql_query($anotherSql)) {
                    $anothers = array();
                    while($anotherResult_array = mysql_fetch_object($anotherResult)) {
                        $another = array();
                        foreach($anotherResult_array as $anotherKey => $anotherValue) {
                            $another[$anotherKey] = $anotherValue;
                        }
                        $anothers[] = $another;
                    }
                    $entries[$key] = $anothers;
                }
            }
            else {
                $entries[$key] = array();
            }
        }
        else {
            $entries[$key] = $value;
        }
    }
    $posts[] = $entries;
}

任何建议都表示赞赏。我仍在寻找加入并离开加入解决方案!

1 个答案:

答案 0 :(得分:1)

这实际上取决于你正在寻找的东西:

所有帖子的用户数据:

SELECT user.*, post.* 
FROM post
LEFT JOIN user ON (post.publisherid=user.id)

因为每个帖子只有一个发布商,所以这应该为每个帖子提供用户的数据。

liker ids

SELECT post.*, GROUP_CONCAT(likes.likerid) as likerids
FROM post
LEFT JOIN likes ON (likes.postid=post.pid)
GROUP BY post.pid

这会给你行:

["pid" => 3, "publisherid" => 12, "likerids" => "15,17,19"]

然后你需要在php中做的就是:

$likerids = explode(',', $row['likerids']);

结合娱乐和利润

当然,您可以将两个查询合并为一个。但是,如果您只需要喜欢的ID,则第二个查询只能正常工作。如果您还想要用户数据,可能是好的(取决于您的实际用例),先收集likerids并稍后获取其用户数据

SELECT *
FROM user 
WHERE user.uid IN (15,17,19)

另外,你真的应该真的使用准备好的语句来防止sql注入。(这不是偶然的大胆!这很重要)如果你不知道sql注入是什么,读一读。如果有人发现查询容易受到用户提供的输入和sql注入,那么您的所有用户都可以数据可能(并且很可能会)泄漏到互联网的黑暗中。

另外,请使用pdo或mysqli库进行数据库查询。不推荐使用mysql库,并在7中消失了。[相信]我相信。

更新

获取m:n关系的两侧有很多问题。我的意思是,基本上它很简单,只需抓取它:

SELECT post.*, user.*
FROM post
LEFT JOIN likes ON (post.pid=likes.postid)
LEFT JOIN user ON (likes.likerid=user.uid)
ORDER BY post.pid

然而,这将产生这些行:

pid1, publisherid1, userid1, username1
pid1, publisherid1, userid2, username2
...
pid2, publisherid2, userid1, username1
...

正如您将注意到的那样,帖子本身会多次出现,每个人都会出现一次。这是一个问题,由于sql的基本原理(基于行),单独使用标准sql是无法避免的。

这基本上就是你想要的数据,但我认为是一种更加聚合的形式。此表单还包含大量冗余数据,尤其是假设后期数据大于用户数据。要收集数据,您必须检查每一行的pid,如果它与之前的行中的pid相同,则以某种方式合并记录。 ......但我强烈反对这种做法。

我还建议不要对每个用户字段使用GROUP_CONCAT,尽管它可能有用。问题是,GROUP_CONCAT需要一个分隔符,您需要与用户名字段中的任何字符(或您要检索的任何其他字段)不同。这可能是也可能不是问题,但它仍然很脏。在任何情况下,您都必须在php中爆炸每个聚合字段,重建用户'建立你想要的结构的数据。

另一种解决方案可能是,创建一个新的字段,将聚合的用户数据保存为json或其他东西,并且通过智能使用GROUP_CONCATCONCAT,可以为每一行创建一个分层字符串,这可能是json本身。但这超出了这个职位。 (此外,我还宽恕了这样的数据库的使用,这些数据库不是为此而设计的。但是有一个JSON数据类型,可能很有趣......

最终,在这些情况下,您让数据库服务器完成客户端应该完成的工作。

我会这样做:

两个查询,用于有限数量的用户(因为YAGNI)

首先我们要获取我们想要的帖子,我们还会为喜欢添加计数,并且还会包含发布商的用户数据(如果您添加WHERE数据,它来自服务器外部,如浏览器,使用预备语句!也读取SQL,如果你不理解这个查询的全部或部分!) - 我想,这是你要显示的所有数据一开始是用户。 (借助缓存的强大功能,为不同的帖子展示喜欢的人可能非常有效。)

$pdo = new PDO('#yourdatabasestring#'); // rtfm!
$postresult = $pdo->query(
   'SELECT p.*, '.
   '   pub.uid, pub.username, '.
   '   COUNT(likers.uid) as likecount '.
   'FROM post p '.
   'LEFT JOIN user as pub ON (pub.uid=post.publisherid) '.
   'LEFT JOIN likes ON (post.pid=likes.postid) '.
   'LEFT JOIN user as likers ON (likers.uid=likes.likerid) '
   'GROUP BY p.pid '.
   'LIMIT 50'    // learn about offsets!!!
);

现在,将所有结果放入数组

$pids = []; // this will contain post ids for which we want to fetch likes
$posts = [];
while($post = $postresult->fetch()) {
   $pids[] = $post['pid'];
   $post['likers'] = []; // prepare for later
   $posts[$post['pid']] = $post;
}

此时,此数组仅包含第一个查询中请求的数据(发布者,发布者的用户数据)。接下来,我们查询喜欢的内容,我们使用临时存储的帖子ID。*

$likers = $pdo->query(
    'SELECT likes.postid, user.* '.
    'FROM likes '.
    'LEFT JOIN user ON (likes.likerid=user.uid) '.
    'WHERE likes.postid IN ('.implode(',', $pids).')'
);

并获取它们并将它们分配到正确的帖子。

while($like = $likers->fetch()) {
    $posts[$like['postid']]['likers'][] = $like;
}

现在......这个解决方案实际上应该适用于几乎每个sql数据库。 GROUP_CONCAT在这里没有任何好处。这里有两个查询非常好。如果您想要一次性获取大量帖子,这可能绝对不是正确的方法。对于相当小的数据集(大约几百个帖子),这应该是非常好的。

*)WHERE子句可以替换为WHERE postid IN ([first query with only poist.pid in select])。对于某些用例,这可能更合适。

建议

然而,对于通常的网络案例,我无法想象有人想要同时看到超过50个帖子已经显示的用户数据,喜欢'数据和东西。不要试图一次展示一切。获取必要的内容,尝试聚集信息(就像我对$pids所做的那样)以减少查询次数。做一些设计良好且运行时间短的查询通常可以执行许多查询(如原始代码中所示),但也比运行一个大型查询更合适,大多数数据(平均)将无关紧要。