Возможно ли это сделать в одном запросе MySQL?

#php #mysql

#php #mysql

Вопрос:

У меня есть две таблицы:

conversation_participants

 id      | conversation_id | user_id
int(11) |  int(11)        |   int(11)
 

Пользователи

 id      | username   | ....
int(11) | varchar(20)| ....
 

Я пытаюсь выбрать всех пользователей, у которых есть определенная строка в их имени пользователя, которые (пока) не являются участниками определенного разговора. В настоящее время у меня есть два SQL-запроса:

 SELECT user_id FROM conversation_participants WHERE conversation_id=?
 

и

 SELECT id, username from users WHERE username LIKE '%%%{$_GET['q']}%%' ORDER BY id DESC LIMIT 10
 

И удалите все результаты из второго запроса, которые имеют тот же user_id, что и в первом запросе. Есть ли какой-либо способ объединить эти два запроса в один?

Заранее благодарю

Комментарии:

1. Я действительно надеюсь, что мы должным образом очистим $_GET['q'] его, прежде чем использовать в SQL-запросе…

2. Смотрите ниже, это было просто в тесте. Я использую подготовленные инструкции mysqli в производстве.

Ответ №1:

 SELECT id, username from users 
WHERE username LIKE '%%%{$_GET['q']}%%' 
and username not in 
(
  SELECT user_id FROM conversation_participants 
  WHERE conversation_id=users.conversation_id
)
ORDER BY id DESC LIMIT 10
 

Не создавайте свои операторы sql, объединяющие строки. Вы должны использовать какой-то служебный метод для очистки вашего ввода. Ваше утверждение подвержено атакам sql-инъекций.

В PHP есть несколько функций для этого. Загляните в mysql_real_escape_string

Комментарии:

1. Спасибо! Хотя это было не совсем то, что мне было нужно, это помогло мне определить нужный мне запрос. Вот мой последний (отредактированный) запрос: SELECT id, username FROM users WHERE username LIKE '%%%{$_GET['q']}%%' AND id NOT IN ( SELECT user_id FROM conversation_participants WHERE conversation_id = ? ) ORDER BY id DESC

Ответ №2:

В дополнение к приведенному выше ответу я должен добавить, что вы не должны использовать параметры $ _GET (etc) непосредственно в своем запросе, не проверяя его на наличие SQL-инъекций.

Комментарии:

1. Использовался только для тестирования, а не в производстве, как вы можете видеть из моего другого запроса (вопросительный знак) Я использую mysqli с подготовленными операторами.

Ответ №3:

Вы можете сделать это с помощью ЛЕВОГО СОЕДИНЕНИЯ вместе с IS NULL

 SELECT 
    u.id, 
    u.username 
from 
    users u
    LEFT JOIN
         conversation_participants cp 
    ON (u.id=cp.user_id AND cp.conversation_id=?)
WHERE 
    username LIKE '%%%{$_GET['q']}%%' AND
    cp.user_id IS NULL
ORDER BY id DESC LIMIT 10
 

Я считаю, что это более оптимальное решение, чем использование NOT IN() и подзапроса по соображениям производительности.

Ответ №4:

Вы также можете использовать JOIN, что может быть немного быстрее.

 SELECT u.id, u.username from users u LEFT JOIN conversation_participants c ON c.user_id = u.id WHERE u.username LIKE '%%%{$_GET['q']}%%' AND c.conversation_id != ? ORDER BY u.id DESC LIMIT 10
 

Комментарии:

1. Этот запрос, похоже, не работает для меня, во-первых, я получаю каждого пользователя несколько раз (но ничего такого, что нельзя было бы исправить с помощью DISTINCT), но также он не удаляет пользователей, которые уже являются участниками (в чем и заключалась вся идея).

2. Ты прав. Это не сработало бы, если бы в таблице conversation_participants было несколько записей для пользователя.

Ответ №5:

В дополнение к приведенному выше ответу, вы могли бы удалить свое поле «id» в таблице conversation_participants и определить 2 поля conversation_id и user_id в качестве первичного ключа.

Комментарии:

1. Какие-либо конкретные причины, по которым это было бы лучше?