Оператор Mysql «Like», дважды возвращающий одни и те же строки

avatar
malacai79
8 августа 2021 в 18:57
40
1
1

Я пытаюсь отобразить отдельный набор строк в PHP на основе результатов запроса MySQL, но вижу повторяющиеся записи, выводимые в HTML.

Мой код PHP и MySQL выглядит следующим образом:

<h2>Related products</h2>
<?php
    $title=str_replace(' ',',',$_GET['title']);
    $words=explode(',',$title);
    foreach ($words as $word){
    if (strlen($word) > 5){ 
    $res=mysqli_query($link, "SELECT title,id FROM posts WHERE title LIKE '% $word %' or title LIKE '%$word' or title LIKE '$word%' ORDER BY id DESC");
    while ($row=mysqli_fetch_array($res)){
        echo "<a href='/product/".$row['id']."/'><h3>".$row['title']."</h3></a>";
    }}}
?>

Я искал уже заданные похожие вопросы и, основываясь на их рекомендациях, попытался подать заявку:

Select DISTINCT.. 

А также:

while ($row=array_unique(mysqli_fetch_array($res)))

Однако он по-прежнему показывает повторяющиеся результаты. (Один и тот же товар отображается 3-5 раз в разделе сопутствующих товаров.)

Источник
sticky bit
8 августа 2021 в 18:59
4

(Возможное) примечание: не используйте интерполяцию или конкатенацию строк для получения значений в запросах SQL. Это подвержено ошибкам и может сделать вашу программу уязвимой для атак SQL-инъекций. Используйте параметризованные запросы. См. "Как включить переменную PHP в инструкцию MySQL" и "Как предотвратить внедрение SQL в PHP?".

sticky bit
8 августа 2021 в 19:01
1

Отредактируйте вопрос и предоставьте минимальный воспроизводимый пример, то есть CREATE операторы таблиц или других задействованных объектов (вставьте текст, не используйте изображения, не ссылайтесь на внешние сайты), INSERT инструкции для выборочных данных (то же) и желаемый результат с этими примерными данными в табличном текстовом формате.

malacai79
8 августа 2021 в 19:09
0

В конце концов я добавлю mysqli_real_eascape_string в $_GET['title'] для предотвращения SQL-инъекций. но я не уверен, что мне нужно уточнить в этом посте

sticky bit
8 августа 2021 в 19:11
1

Нет, забудьте ...real_eascape... и используйте параметризованные запросы!

sticky bit
8 августа 2021 в 19:12
0

Уточните, как я вам сказал. Что конкретно вам в нем не понятно?

Tim Morton
8 августа 2021 в 19:16
2

Вы выполняете запрос n раз, по одному разу для каждого слова. Таким образом, если заголовок соответствует более чем одному слову, он будет совпадать при каждой итерации. Вместо того, чтобы повторять запрос, вам нужно повторить, чтобы создать одно предложение where

Tim Morton
8 августа 2021 в 19:18
1

… и сейчас самое время начать использовать параметризованный запрос, потому что вам все равно нужно изменить запрос. Может и правильно сделать.

Ответы (1)

avatar
Tim Morton
8 августа 2021 в 20:01
0

Первоначальная проблема заключается в том, что вы повторяете запрос вместо создания одного запроса.

В этом коде показано построение запроса с использованием параметризованного запроса.

Обратите внимание, что я не могу это проверить; могут потребоваться некоторые корректировки.

<?php
// initialize $title to prevent uninitialized variable warning in while loop
$title = '';

if(isset($_GET['title']) {

    // make pdo connection here; assign to $pdo
    // left to reader to implement 

    $title=str_replace(' ',',',$_GET['title']);
    $words=explode(',',$title);

    $where = [];
    $values = [];
    foreach ($words as $word) {
        if (strlen($word) < 5) { 
            continue;
        }
        $where[] = 'TITLE LIKE ?';
        $values[] = '%' . $word . '%';
    }

    $where = join(' OR ', $where);

    $stmt = $pdo->prepare("SELECT TITLE, ID FROM POSTS WHERE $where");
    $stmt->execute($values);
}
?>

<h2>Related products</h2>
<?php while($title && ($row = $stmt->fetch(PDO::FETCH_ASSOC);)): ?>
    <a href="product<?= $row['id'] ?>">
        <h3><?= $row['title'] ?></h3>
    </a>
<?php endwhile; ?>