Как я могу сделать подзапрос с EXISTS на Knex?

#javascript #sql #node.js #typescript #knex.js

Вопрос:

Я пытаюсь построить запрос с помощью knex, но у меня много проблем с тем, как реализовать часть «ГДЕ» с условием EXISTS (SELECT * FROM caregiver_patient WHERE patient_id IN (0,1)) .

Вот исходный запрос на SQL:

 SELECT * FROM users 
    JOIN caregivers ON users.id = caregivers.user_id
    JOIN caregiver_schedule ON caregivers.id = caregiver_schedule.caregiver_id 
    JOIN caregiver_patient ON caregivers.id = caregiver_patient.caregiver_id
    JOIN patients ON caregiver_patient.patient_id = patients.id
    WHERE caregiver_schedule.week_day = 2
    AND caregiver_schedule.from_time <= 1320
    AND caregiver_schedule.to_time > 1320
    AND EXISTS (SELECT * FROM caregiver_patient WHERE patient_id IN (0,1));
 

И вот код, который у меня есть до сих пор с Knex:

 const caregivers = await db("caregivers")
  .whereExists(function () {
    this.select("caregiver_schedule.*")
      .from("caregiver_schedule")
      .whereRaw("`caregiver_schedule`.`caregiver_id` = `caregivers`.`id`")
      .whereRaw("`caregiver_schedule`.`week_day` = ??", [Number(week_day)])
      .whereRaw("`caregiver_schedule`.`from_time` <= ??", [timeInMinutes])
      .whereRaw("`caregiver_schedule`.`to_time` > ??", [timeInMinutes]);
  })
  .join("users", "caregivers.user_id", "=", "users.id")
  .join("patients", "caregiver_patient.patient_id", "=", "patients.id")
  .select([
    "caregivers.*",
    "users.*",
    "caregiver_schedule.*",
    "patients.*",
  ]);
 

Все числа в исходном запросе должны быть переменными. Числа внутри IN должны быть массивом.

Кто-нибудь может помочь мне закончить создание этого запроса на Knex?

Ответ №1:

Приведенный вами пример запроса knex на самом деле не очень похож на исходный SQL-запрос, вот как выглядел бы ваш исходный SQL-запрос, если бы вы хотели преобразовать его в запрос knex:

 const result = await db('users')
    .innerJoin('caregivers', 'users.id', 'caregivers.user_id')
    .innerJoin('caregiver_schedule', 'caregivers.id' 'caregiver_schedule.caregiver_id')
    .innerJoin('caregiver_patient', 'caregivers.id', 'caregiver_patient.caregiver_id')
    .innerJoin('patients', 'caregiver_patient.patient_id', 'patients.id')
    .where('caregiver_schedule.week_day', Number(week_day))
    .where('caregiver_schedule.from_time', '<=', timeInMinutes)
    .where('caregiver_schedule.to_time', '>', timeInMinutes)
    .whereExists(function() {
        this.select('*')
        .from('caregiver_patient')
        .whereIn('patient_id', [0,1]);
    })
    .select('*');

 

Это предполагает, что db переменная является вашим соединением knex. Мне также пришлось угадать переменные, которые вы хотели использовать для замены жестко закодированных значений в исходном SQL, не стесняйтесь заменять переменные любыми, которые вам нравятся.

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

1. Спасибо вам за ответ! Кроме того, знаете ли вы, как я могу убедиться, что выбранные регистры во внутреннем выборе связаны только с текущим регистром (воспитателем) Например, я хотел чего-то подобного: AND EXISTS (SELECT * FROM caregiver_patient WHERE patient_id IN (1) AND caregiver_id=caregivers.user_id); я не могу сделать это с knex

2. Вы должны просто иметь возможность вставить другой .where запрос в указанный выше запрос внутри whereExists функции, с той лишь разницей, что, когда вы хотите ссылаться на другой столбец в where предложении, вам нужно использовать немного другой синтаксис, иначе он попытается сопоставить его со строковым литералом "caregivers.user_id" , а не со столбцом. Есть два способа сделать это, первый-использовать .ref , например .where('caregiver_id', knex.ref('caregivers.user_id')); , другой вариант-использовать позиционные аргументы и, например, knex.raw .where('caregiver_id', knex.raw('??', 'caregivers.user_id')); .