Добавление условий к ОБЪЕДИНЕНИЮ в MySQL

#mysql #sql

#mysql #sql

Вопрос:

Я не уверен, возможно ли то, что я хочу сделать, с объединением, или мне нужно использовать вложенный запрос и какое-то объединение.

 select c1,c2 from t1
union
select c1,c2 from t2
// with some sort of condition where t1.c1 = t2.c1
  

Пример:

 t1
| 100 | regular |
| 200 | regular |
| 300 | regular |
| 400 | regular |

t2
| 100 | summer |
| 200 | summer |
| 500 | summer |
| 600 | summer |

Desired Result
| 100 | regular |
| 100 | summer  |
| 200 | regular |
| 200 | summer  |
  

Я пробовал что-то вроде:

 select * from (select * from t1) as q1
inner join
    (select * from t2) as q2 on q1.c1 = q2.c1
  

Но это объединяет записи в одну строку вот так:

 | 100 | regular | 100 | summer |
| 200 | regular | 200 | summer |
  

Ответ №1:

Попробуйте:

 select c1, c2
  from t1
 where c1 in (select c1 from t2)
union all
select c1, c2
  from t2
 where c1 in (select c1 from t1)
  

На основе редактирования попробуйте следующее:

В MySQL нет предложения WITH, которое позволило бы вам ссылаться на ваши вспомогательные модули t1 и t2 несколько раз. Возможно, вам захочется создать оба t1 и t2 в качестве представления в вашей базе данных, чтобы вы могли ссылаться на них как на t1 и t2 несколько раз в течение одного запроса.

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

Например, в вашем подразделении t1 у вас есть внешнее объединение с таблицей LESSON, но тогда у вас есть критерии в вашем предложении WHERE (урок.dayofweek > = 0), который, естественно, не допускал бы нулей, эффективно превращая ваше внешнее соединение во внутреннее соединение. Также у вас есть подзапросы, которые проверяют только существование studentid, используя критерии, которые предполагают, что несколько используемых таблиц на самом деле не обязательно использовать для получения желаемого результата. Однако, не зная структуры вашей базы данных и некоторых примеров данных с ожидаемым результатом, трудно давать дальнейшие рекомендации.

Тем не менее, я считаю, что приведенное ниже, вероятно, даст вам то, что вы хотите, просто не оптимально.

 select *
  from (select distinct students.student_number as "StudentID",
                        concat(students.first_name, ' ', students.last_name) as "Student",

                        general_program_types.general_program_name as "Program Category",
                        program_inventory.program_code as "Program Code",
                        std_lesson.studio_name as "Studio",
                        concat(teachers.first_name, ' ', teachers.last_name) as "Teacher",

          from lesson_student
          left join lesson
            on lesson_student.lesson_id = lesson.lesson_id
          left join lesson_summer
            on lesson_student.lesson_id = lesson_summer.lesson_id

         inner join students
            on lesson_student.student_number = students.student_number
         inner join studio as std_primary
            on students.primary_location_id = std_primary.studio_id
         inner join studio as std_lesson
            on (lesson.studio_id = std_lesson.studio_id or
               lesson_summer.studio_id = std_lesson.studio_id)

         inner join teachers
            on (lesson.teacher_id = teachers.teacher_id or
               lesson_summer.teacher_id = teachers.teacher_id)
         inner join lesson_program
            on lesson_student.lesson_id = lesson_program.lesson_id
         inner join program_inventory
            on lesson_program.program_code_id =
               program_inventory.program_code_id
         inner join general_program_types
            on program_inventory.general_program_id =
               general_program_types.general_program_id

         inner join accounts
            on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
         inner join account_contacts
            on students.ACCOUNT_NUMBER = account_contacts.ACCOUNT_NUMBER

        /** NOTE: the WHERE condition is the only **/
        /** difference between subquery1 amp; subquery2 **/
         where lesson.dayofweek >= 0 and
         order by students.STUDENT_NUMBER) t1
 where StudentID in
       (select StudentID
          from (select distinct students.student_number as "StudentID",
                                concat(students.first_name,
                                       ' ',
                                       students.last_name) as "Student",

                                general_program_types.general_program_name as "Program Category",
                                program_inventory.program_code as "Program Code",
                                std_lesson.studio_name as "Studio",
                                concat(teachers.first_name,
                                       ' ',
                                       teachers.last_name) as "Teacher",

                  from lesson_student
                  left join lesson
                    on lesson_student.lesson_id = lesson.lesson_id
                  left join lesson_summer
                    on lesson_student.lesson_id = lesson_summer.lesson_id

                 inner join students
                    on lesson_student.student_number =
                       students.student_number
                 inner join studio as std_primary
                    on students.primary_location_id = std_primary.studio_id
                 inner join studio as std_lesson
                    on (lesson.studio_id = std_lesson.studio_id or
                       lesson_summer.studio_id = std_lesson.studio_id)

                 inner join teachers
                    on (lesson.teacher_id = teachers.teacher_id or
                       lesson_summer.teacher_id = teachers.teacher_id)
                 inner join lesson_program
                    on lesson_student.lesson_id = lesson_program.lesson_id
                 inner join program_inventory
                    on lesson_program.program_code_id =
                       program_inventory.program_code_id
                 inner join general_program_types
                    on program_inventory.general_program_id =
                       general_program_types.general_program_id

                 inner join accounts
                    on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
                 inner join account_contacts
                    on students.ACCOUNT_NUMBER =
                       account_contacts.ACCOUNT_NUMBER

                /** NOTE: the WHERE condition is the only **/
                /** difference between subquery1 amp; subquery2 **/
                 where lesson_summer.dayofweek >= 0
                 order by students.STUDENT_NUMBER) t2)
UNION ALL
select *
  from (select distinct students.student_number as "StudentID",
                        concat(students.first_name, ' ', students.last_name) as "Student",

                        general_program_types.general_program_name as "Program Category",
                        program_inventory.program_code as "Program Code",
                        std_lesson.studio_name as "Studio",
                        concat(teachers.first_name, ' ', teachers.last_name) as "Teacher",

          from lesson_student
          left join lesson
            on lesson_student.lesson_id = lesson.lesson_id
          left join lesson_summer
            on lesson_student.lesson_id = lesson_summer.lesson_id

         inner join students
            on lesson_student.student_number = students.student_number
         inner join studio as std_primary
            on students.primary_location_id = std_primary.studio_id
         inner join studio as std_lesson
            on (lesson.studio_id = std_lesson.studio_id or
               lesson_summer.studio_id = std_lesson.studio_id)

         inner join teachers
            on (lesson.teacher_id = teachers.teacher_id or
               lesson_summer.teacher_id = teachers.teacher_id)
         inner join lesson_program
            on lesson_student.lesson_id = lesson_program.lesson_id
         inner join program_inventory
            on lesson_program.program_code_id =
               program_inventory.program_code_id
         inner join general_program_types
            on program_inventory.general_program_id =
               general_program_types.general_program_id

         inner join accounts
            on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
         inner join account_contacts
            on students.ACCOUNT_NUMBER = account_contacts.ACCOUNT_NUMBER

        /** NOTE: the WHERE condition is the only **/
        /** difference between subquery1 amp; subquery2 **/
         where lesson_summer.dayofweek >= 0
         order by students.STUDENT_NUMBER) x
 where StudentID in
       (select StudentID
          from (select distinct students.student_number as "StudentID",
                                concat(students.first_name,
                                       ' ',
                                       students.last_name) as "Student",

                                general_program_types.general_program_name as "Program Category",
                                program_inventory.program_code as "Program Code",
                                std_lesson.studio_name as "Studio",
                                concat(teachers.first_name,
                                       ' ',
                                       teachers.last_name) as "Teacher",

                  from lesson_student
                  left join lesson
                    on lesson_student.lesson_id = lesson.lesson_id
                  left join lesson_summer
                    on lesson_student.lesson_id = lesson_summer.lesson_id

                 inner join students
                    on lesson_student.student_number =
                       students.student_number
                 inner join studio as std_primary
                    on students.primary_location_id = std_primary.studio_id
                 inner join studio as std_lesson
                    on (lesson.studio_id = std_lesson.studio_id or
                       lesson_summer.studio_id = std_lesson.studio_id)

                 inner join teachers
                    on (lesson.teacher_id = teachers.teacher_id or
                       lesson_summer.teacher_id = teachers.teacher_id)
                 inner join lesson_program
                    on lesson_student.lesson_id = lesson_program.lesson_id
                 inner join program_inventory
                    on lesson_program.program_code_id =
                       program_inventory.program_code_id
                 inner join general_program_types
                    on program_inventory.general_program_id =
                       general_program_types.general_program_id

                 inner join accounts
                    on students.ACCOUNT_NUMBER = accounts.ACCOUNT_NUMBER
                 inner join account_contacts
                    on students.ACCOUNT_NUMBER =
                       account_contacts.ACCOUNT_NUMBER

                /** NOTE: the WHERE condition is the only **/
                /** difference between subquery1 amp; subquery2 **/
                 where lesson.dayofweek >= 0 and
                 order by students.STUDENT_NUMBER) x);
  

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

1. Я обновил свой вопрос, включив в него подзапросы, для которых t1 и t2 на самом деле являются псевдонимами. Когда я попробовал ваше решение, оно сообщило мне «Таблица <mydb>.t2 не существует. Я так понимаю, это какая-то проблема с областью псевдонимов таблиц. Имеет ли это смысл?

2. @Kevin замените t2 на любое другое имя таблицы. В вашем примере вы назвали это как t2

3. t1 и t2 — это не таблицы в моей базе данных, это псевдонимы, которые я создал для своих двух подзапросов.

4. Если вы измените свой пример, включив в него фактические таблицы и желаемый результат, я могу изменить ответ.

Ответ №2:

Чтобы сравнить значения из двух таблиц, вам нужно выполнить объединение. Первый способ, который я вижу, это выполнить 2 запроса с внутренним объединением и объединить их:

 select t1.* from t1
inner join t2 using (c1)
union
select t2.* from t1
inner join t2 using (c1)
  

Однако мы можем сделать лучше: вы можете видеть, что я использую точно такой же запрос, вплоть до того, какие столбцы я выбираю в результирующем наборе, это пустая трата времени. Если бы мы могли просто дублировать все записи(1)? Ну, мы можем:

 select * from t1
inner join t2 using (c1)
cross join (
    select 0 as parity
    union
    select 1 as parity) dup
  

Теперь у нас есть все в формате double, с дополнительным столбцом для четности, нам просто нужно выбрать любой набор столбцов в этом:

 -- broken query
select case parity when 
   0 then t1.*
   1 then t2.*
end from t1
inner join t2 using (c1)
cross join (
    select 0 as parity
    union
    select 1 as parity) dup
  

К сожалению, case выражение не позволяет возвращать несколько столбцов, поэтому нам нужно включить тест для всех из них. В вашем случае достаточно одного столбца:

 select c1, case parity when 
   0 then t1.c2
   1 then t2.c2
end from t1
inner join t2 using (c1)
cross join (
    select 0 as parity
    union
    select 1 as parity) dup
order by c1
-- order by parity, c1
  

Используйте закомментированное значение order by вместо этого, чтобы получить результирующий набор для t1 first, а затем t2 .

Предостережение: код не протестирован.


(1) В ANSI SQL есть функция под названием CTE (я думаю, 99), которая позволяет выделить общий запрос и затем ссылаться на него во всем основном запросе, но, к сожалению, mysql ее не поддерживает.

Ответ №3:

UNION Это нормально.

Вы хотите выполнить одно и то же объединение дважды. За исключением того, что в первый раз вы получаете левую сторону, а во второй раз — правую.

 SELECT t1.* FROM t1 JOIN t2 USING (c1)
UNION
SELECT t2.* FROM t1 JOIN t2 USING (c1)
  

Конечно, если это вообще возможно, вы могли бы выполнить один запрос и сохранить левую часть в памяти, отобразить правую часть, а затем поставить сохраненную левую часть в очередь в конце. Для этого требуется гораздо больше памяти, чем для курсора, но запрос выполняется в два раза быстрее (что-то меньшее на самом деле из-за кэширования диска и ресурсов).

Смотрите здесь пример SQLFiddle.