简体   繁体   English

如何基于INNER JOIN SQL查询或php中的一张表来限制页面结果的数量?

[英]How to limit the number of page results based on one table in INNER JOIN SQL query or in php?

I am trying to get 5 questions per page with answers (one to many relational for questions and answers table) but, i am getting the number of records per page for this join table, is there anyway to limit the results based on questions table for pagination. 我正在尝试每页包含答案的问题5个(问题和答案表一对一的关系),但是,我正在获得此联接表每页的记录数,无论如何,是否有限制基于问题表的结果分页。

<?php
$topic_id = $_GET['topic_id'];
$answers_data = [];
$questions_data = [];
if (isset($_GET["page"])) { $page  = $_GET["page"]; } else { $page=1; }; 
$num_rec_from_page = 5;
$start_from = ($page-1) * $num_rec_per_page;
$sql = "SELECT questions.q_id,questions.question,answers.answers,answers.answer_id FROM questions INNER JOIN answers ON questions.q_id = answers.q_id WHERE topic_id='$topic_id' LIMIT $start_from, $num_rec_from_page";
$result = $connection->query($sql);
while($row = mysqli_fetch_assoc($result)) {
$data[] = $row;
}//While loop

foreach($data as $key => $item) {
    $answers_data[$item['q_id']][$item['answer_id']] = $item['answers'];
}
foreach($data as $key => $item) {
    $questions_data[$item['q_id']] = $item['question'];
}
?>

I am get results for above query data using 2 for-each loops as below. 我使用以下2个for-each循环获取上述查询数据的结果。

<?php
$question_count= 0;
foreach ($answers_data as $question_id => $answers_array) {
$question_count++;
$q_above_class = "<div class='uk-card-default uk-margin-bottom'><div class='uk-padding-small'><span class='uk-article-meta'>Question :".$question_count."</span><p>";
$q_below_class = "</p></span><div class='uk-padding-small'>";

echo $q_above_class.$questions_data[$question_id].$q_below_class;

$answer_count = 0;
foreach($answers_array as $key => $answer_options) {
$answer_count++;
$answer_options = strip_tags($answer_options, '<img>');
$ans_above_class="<a class='ansck'><p class='bdr_lite uk-padding-small'><span class='circle'>".$answer_count."</span>";
$ans_below_class = "</p></a>";
  echo $ans_above_class.$answer_options.$ans_below_class; 
}
echo "</div></div></div>";   
}
?>

Is there any idea, how can i limit the results per page, based on questions table. 有什么想法,我如何限制基于问题表的每页结果。

something like this 像这样的东西

SELECT
    q.q_id,
    q.question,
    a.answers,
    a.answer_id
FROM
    (
        SELECT
            q_id, question
        FROM
            questions
        WHERE
            topic_id=:topic_id           
        LIMIT
            $start_from, $num_rec_from_page
    ) AS q
JOIN
    answers AS a ON q.q_id = a.question_id

A few questions/thoughts/notes. 一些问题/想法/笔记。

  • you had question.q_id and question.question_id which seems like an error. 您有question.q_idquestion.question_id ,这似乎是一个错误。 So I just went with q_id the other one is more typing (which I don't like) I had a 50-50 chance I figured... so 所以我只用了q_id ,另一个是更多的打字(我不喜欢),我有50-50的机率……

  • you had just topic_id so I can't be sure what table it's from, I'm assuming it's from table "question"? 您只有topic_id所以我不确定它来自哪个表,我假设它来自表“ question”? It makes a big difference as we really need the where condition on the sub-query where the limit is. 差异很大,因为我们确实需要子查询的where条件限制在哪里。

  • Inner Join, is the same thing as a Join, so I just put Join because I'm lazy. 内部联接与联接是同一回事,所以我放联接是因为我很懒。 I found this previous post ( click here ) on SO that talks about it 我在SO上找到了上一篇有关它的文章( 请单击此处

  • :topic_id I parameterized your query, I don't do variable concatenation and SQLInjection vulnerability stuff. :topic_id我参数化了您的查询,但不进行变量串联和SQLInjection漏洞之类的工作。 (aka. please use prepared statements ) Named placeholders are for PDO, that's what I like using, you can pretty much just replace it with a ? (又名,请使用准备好的语句 )命名占位符用于PDO,这就是我喜欢使用的占位符,您几乎可以将其替换为? for mysqli 对于mysqli

  • as I said with INNER JOIN, I'm lazy so I like aliasing my tables with just 1 character, so that was what I did. 就像我对INNER JOIN所说的那样,我很懒,所以我喜欢只用1个字符来别名表,这就是我所做的。 ( I think you don't even need the AS part, but I'm not "that" lazy). (我认为您甚至不需要AS部分,但我并不“懒”)。 Sometimes I have to use 2, which really irritates me, but whatever 有时我必须使用2,这确实让我感到恼火,但无论如何

With a sub-query, you can just limit the rows from that table, then you join the results of that query back to the main query like normal. 使用子查询,您可以只限制该表中的行,然后像平常一样将查询的结果连接回主查询。 This way you pull 5 or what have you from question table, and then {n} rows from answer based only on the join to the results of the inner query. 这样,您从问题表中拉出5或有什么,然后仅基于对内部查询结果的联接而从答案中提取{n}行。

Cant really test it, but in theory it should work. 不能真正测试它,但是从理论上讲它应该可以工作。 You'll have to go though the results, and group them by question. 您必须仔细检查结果,然后按问题分组。 Because you will get {n} rows that have the same 5 questions joined in them. 因为您将获得{n}行,其中包含相同的5问题。 With PDO, you could do PDO::FETCH_GROUP I don't think Mysqli has an equivalent method so you'll have to do it manually, but it's pretty trivial. 使用PDO,您可以执行PDO::FETCH_GROUP我认为Mysqli没有等效的方法,因此您必须手动进行操作,但这非常简单。

UPDATE 更新

Here is a DB fiddle I put to gather you can see it does exactly what you need it to 这是我收集的数据库小提琴,您可以看到它完全满足您的需要

https://www.db-fiddle.com/f/393uFotgJVPYxVgdF2Gy2V/3 https://www.db-fiddle.com/f/393uFotgJVPYxVgdF2Gy2V/3

Also I put a non-subquery below it to show the difference. 另外,我在其下放置了一个非子查询以显示差异。

As for things like small syntax errors and table/column names, well I don't have access to your DB, you're going to have to put some effort in to adapt it to your setup. 至于小语法错误和表/列名之类的问题,那么我无权访问您的数据库,您将不得不付出一些努力以使其适应您的设置。 The only information I have is what you put in the question and so your question had the wrong table/column name. 我仅有的信息是您在问题中输入的内容,因此您的问题的表/列名称错误。 I already pointed several of these issues out before. 我之前已经指出了其中一些问题。 I'm not saying that to be mean or condescending, it's just a blunt fact. 我并不是说刻薄或屈尊,这只是一个直白的事实。

UPDATE1 更新1

Based on you comment. 根据您的评论。 in 1st query the question is redundant

This is just the way the database works, To explain it is very simple, in my example I have 5 questions that match with 8 answers. 这只是数据库的工作方式,要说明这一点非常简单,在我的示例中,我有5个问题与8个答案相匹配。 In any database (not including NoSQL like mongoDB) you can't realistically nest data. 在任何数据库(不包括像mongoDB这样的NoSQL)中,您都无法实际嵌套数据。 In other words you cant pull it like this. 换句话说,您不能像这样拉它。

  question1
      answer1
      answer2
      answer3

You have to pull it flat, and the way that happens is like this 您必须将其拉平,发生的方式是这样的

  question1 answer1
  question1 answer2
  question1 answer3

This is just a natural consequence of how the Database works when joining data. 这只是数据库在连接数据时如何工作的自然结果。 Now that that is out of the way, what do we do about it. 既然这已经不可行了,我们将如何处理。 Because we want the data nested like in the first example. 因为我们希望像第一个示例一样嵌套数据。

  1. We can pull the question, iterate (loop) over the result and do a query for each question and add the data to a sub-element. 我们可以拉出问题,对结果进行迭代(循环),并对每个问题进行查询,然后将数据添加到子元素中。
    • Advantage It's easy to do 优势这很容易做到
    • Disadvantage While this works it's undesirable because we will be making 6 connections to the database (1 to get 5 questions, 1 to get answers for each of the 5 questions) , it requires 2 while loops to process the results, and actually more code. 缺点尽管这样做是不希望的,因为我们将与数据库建立6个连接(1个连接5个问题,1个连接5个问题的每一个) ,它需要2个while循环来处理结果,实际上需要更多的代码。

Psudo code instructions (i don't feel like coding this) 伪代码指令(我不喜欢编码)

       init data variable
       query for our 5 questions
       while each questions as question
           - add question to data
           - query for answers that belong to question
           - while each answers as answer
               -- add answer to nested array in data[question]
       return data
  1. We can process the results and build the structure we want. 我们可以处理结果并构建所需的结构。
    • Advantage We can pull the data in one request 优势我们可以拉一个请求的数据
    • Disadvantage we have to write some code, in #1. 缺点是我们必须在#1中编写一些代码。 we still have to write code, and in fact we have to write more code, because we have to process the DB results 6x (2 while loop) here we need 1 while loop. 我们仍然必须编写代码,并且实际上我们必须编写更多的代码,因为我们必须将数据库结果处理6x(2 while循环),这里我们需要1 while循环。

Psudo code instructions (for comparison) 伪代码指令(用于比较)

       init data variable
       query for our 5 questions and their answers
       while each questions&answers as row
           - check if question is in data
             -- if no, add question with a key we can match to it
           - remove data specific to question (redundant data)
           - add answers to data[question]
       return data

As you can see the basic instructions for the second one are no more complex then the first (same number of instruction). 如您所见,第二条的基本说明并不比第一条复杂(相同数量的指令)。 This is just assuming each line has the same complexity. 这只是假设每行具有相同的复杂性。 Obviously a SQL query or a while loop is more complex then an if condition. 显然,与if条件相比,SQL查询或while循环更复杂。 You'll see below how I convert this psudo code to real code. 您将在下面看到我如何将此伪代码转换为真实代码。 I actually often write psudo code when planing a project. 实际上,我在计划项目时经常写psudo代码。

Anyway, this is what we need to do. 无论如何,这是我们需要做的。 (using the previous SQL or the first one in the fiddle). (使用先前的SQL或小提琴中的第一个)。 Here is your normal "standard" loop to pull data from the DB 这是从数据库中提取数据的常规“标准”循环

 $data = [];
 while($row = mysqli_fetch_assoc($result)) {
     $data[] = $row;
 }//While loop

We will modify this just a bit (it's very easy) 我们将对此进行一点修改(非常简单)

//query for our 5 questions and their answers(using SQL explained above)
//init data variable
$data = [];
//while each questions&answers as row
while($row = mysqli_fetch_assoc($result)) {
   // - create a key based of the question id, for convenience
   $key = 'question_'.$row['q_id'];
   // - check if question is in data
   if(!isset( $data[$key] ) ){
       //--if no, add question with a key we can match to it
       $data[$key] = [
            'q_id' => $row['q_id'],
            'question' => $row['question'],
            'children' => []  //you can call this whatever you want, i choose "children" because there is a field named "answers"
       ];
   }
   //- remove data specific to question (redundant data) [optional]
   unset($data['q_id'], $data['question']);
   //- add answers to data[question]
   $data[$key]['answers'][] = $row;
}
//return data

So what does this look like: For the first while, the standard one, we get this with as you called it redundant data. 所以这是什么样子:在第一个标准时间里,我们将其称为冗余数据。

[
    ["q_id" => "4", "question" => "four", "answers"=>"4", "answer_id"=>"4"],
    ["q_id" => "5", "question" => "five", "answers"=>"5", "answer_id"=>"5"],
    ["q_id" => "5", "question" => "five", "answers"=>"5", "answer_id"=>"6"],
]

For the second one, with our harder code (that's not really hard) we get this: 对于第二个,通过更难的代码(不是很困难),我们得到了:

[
    ["q_id" => "4","question" => "four","children" = ["answers"=>"4","answer_id"=>"4"]],
    [
        "q_id" => "5",
        "question" => "five",
        "children" = [
            "answers"=>"5",
            "answer_id"=>"5"
        ],[
            "answers"=>"5",
            "answer_id"=>"6"
        ]
    ],
]

I expanded the second question so you can see the nesting. 我扩展了第二个问题,以便您可以看到嵌套。 This is also a good example of why it had redundant data, and what is happening in general. 这也是为什么它具有冗余数据以及一般情况的一个很好的例子。 As you can see there is no way to represent 8 rows with 5 shared question without have some redundant data (without nesting them). 如您所见,如果没有一些冗余数据(不嵌套它们),就无法用5个共享问题来表示8行。

The last thing I would like to mention is my choice of $key . 我最后要提到的是我选择的$key We could have used just q_id with no question_ bit added on. 我们本可以仅使用q_id ,而无需添加question_位。 I do this for 2 reasons. 我这样做有两个原因。

  1. It's easier to read when printing it out. 打印出来时更容易阅读。
  2. There are several array_* (and other) functions in PHP that will reset numeric keys. PHP中有几个array_* (和其他)函数将重置数字键。 Because we are storing important data here, we don't want to lose that information. 因为我们在这里存储重要数据,所以我们不想丢失这些信息。 The way to do this is to use strings. 做到这一点的方法是使用字符串。 You can just cast it to a string (int)$row['q_id'] , but in some cases the keys can still get removed. 您可以将其(int)$row['q_id']转换为字符串(int)$row['q_id'] ,但是在某些情况下,键仍然可以删除。 An example is when JSON encoding, there is a flag JSON_FORCE_OBJECT that forces numeric keys to be an object {"0":"value} but it acts global. In any case it can happen where you lose the keys if they are just numbers, and I don't much care for that happening. So I prefix them to prevent that. 例如,当使用JSON编码时,有一个标志JSON_FORCE_OBJECT强制数字键成为对象{"0":"value}但它的作用是全局的。在任何情况下,如果它们只是数字,则可能会丢失键,我对此不太在意,所以我给它们加了前缀以防止发生这种情况。
  3. It's not hard to do something like preg_match('/question_([0-9]+)/', $key, $match) or $id = substr($key, 9); preg_match('/question_([0-9]+)/', $key, $match)$id = substr($key, 9);这样的事情并不难$id = substr($key, 9); to pull it back off of there,We have the q_id in the array,It's no harder to check isset($data['question_1']) then isset($data['1']) , and it looks better. 把它拉回去,我们在数组中有q_id ,检查isset($data['question_1'])然后再检查isset($data['1'])并不困难,它看起来更好。

So for minimum difficulty we can be sure we won't lose our ID's to some code over site (unless we use usort instead of uasort ) but I digress.. 因此,为了最小的困难,我们可以确定我们不会将ID丢失到站点上的某些代码中(除非我们使用usort而不是uasort ),但是我离题了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM