Главная страница
Навигация по странице:

  • 2001-09-13 00:00:00 13 3 1750.00 16 3 1200.00 2001-09-13 00:00:00 12 3 1350.00 17 3 1500.00 2001-09-13 00:00:00

  • UNION

  • Декартово произведение Ранее мы уже рассмотрели реализацию декартова произведения (пункт 5.6

  • FROM

  • Примечание

  • Объединение Для объединения запросов используется служебное слово UNION

  • ORDER BY

  • Type Model price Laptop

  • лекция. Учебник по sql если вы хотите узнать, что такое sql этот сайт для вас


    Скачать 7.88 Mb.
    НазваниеУчебник по sql если вы хотите узнать, что такое sql этот сайт для вас
    Анкорлекция
    Дата21.12.2022
    Размер7.88 Mb.
    Формат файлаpdf
    Имя файлаMoiseenko_SQL.pdf
    ТипУчебник
    #857633
    страница19 из 47
    1   ...   15   16   17   18   19   20   21   22   ...   47
    COALESCE
    :
    1.
    SELECT
    COALESCE
    (
    m_pc.maker, m_printer.maker
    )
    FROM

    2.
    (
    SELECT
    DISTINCT
    maker
    FROM
    Product
    WHERE
    type=
    'pc'
    )
    m_pc
    3.
    FULL
    JOIN
    4.
    (
    SELECT
    DISTINCT
    maker
    FROM
    Product
    WHERE
    type=
    'printer'
    )
    m_printer
    5.
    ON
    m_pc.maker = m_printer.maker
    6.
    WHERE
    m_pc.maker
    IS
    NULL
    OR
    m_printer.maker
    IS
    NULL
    ;
    Разумеется, это не единственный способ решения данной задачи. Он лишь демонстрирует замену репрессированного типа соединений.
    Мне неизвестны СУБД, в которых было бы реализовано соединение UNION
    JOIN.
    Ассоциативность и коммутативность соединений
    Внутреннее и полное внешнее соединения являются как коммутативными, так и ассоциативными, т.е. для них справедливо следующее:
    1. A
    [
    FULL
    |
    INNER
    ]
    JOIN
    B = B
    [
    FULL
    |
    INNER
    ]
    JOIN
    A и
    1.
    (
    A
    [
    FULL
    |
    INNER
    ]
    JOIN
    B
    )
    [
    FULL
    |
    INNER
    ]
    JOIN
    С =
    2. A
    [
    FULL
    |
    INNER
    ]
    JOIN
    (
    B
    [
    FULL
    |
    INNER
    ]
    JOIN
    С
    )
    Очевидно, что левое/правое соединения не коммутативны, т.к.
    1. A
    LEFT
    JOIN
    B = B
    RIGHT
    JOIN
    A но ассоциативны, например:
    1.
    (
    A
    LEFT
    JOIN
    B
    )
    LEFT
    JOIN
    C = A
    LEFT
    JOIN
    (
    B
    LEFT
    JOIN
    C
    )
    С практической точки зрения ассоциативность означает, что мы можем не расставлять скобки, определяющие прядок выполнения соединений.
    Однако закон ассоциативности, справедливый для однотипных соединений, нарушается, если в одном запросе используются соединения разных типов.
    Покажем это на примере.

    1.
    WITH
    a
    (
    a_id
    )
    AS
    2.
    (
    SELECT
    *
    FROM
    (
    VALUES
    (
    '1'
    )
    ,
    (
    '2'
    )
    ,
    (
    '3'
    ))
    x
    (
    y
    ))
    ,
    3. b
    (
    b_id
    )
    AS
    4.
    (
    SELECT
    *
    FROM
    (
    VALUES
    (
    '1'
    )
    ,
    (
    '2'
    )
    ,
    (
    '4'
    ))
    x
    (
    y
    ))
    ,
    5. c
    (
    c_id
    )
    AS
    6.
    (
    SELECT
    *
    FROM
    (
    VALUES
    (
    '5'
    )
    ,
    (
    '2'
    )
    ,
    (
    '3'
    ))
    x
    (
    y
    ))
    7.
    SELECT
    a_id, b_id, c_id
    FROM
    (
    a
    LEFT
    JOIN
    b
    ON
    a_id=b_id
    )
    INNER
    JOIN
    c
    ON
    b_id=c_id
    8.
    UNION
    ALL
    9.
    SELECT
    ''
    ,
    ''
    ,
    ''
    10.
    UNION
    ALL
    11.
    SELECT
    a_id, b_id, c_id
    FROM
    a
    LEFT
    JOIN
    (
    b
    INNER
    JOIN
    c
    ON
    b_id=c_id
    )
    ON
    a_id=b_id;
    a_id
    b_id
    c_id
    2
    2 2
    1
    NULL
    NULL
    2
    2 2
    3
    NULL
    NULL
    Результаты двух запросов отделены друг от друга пробельной строкой для удобства.
    Заметим, что при отсутствии скобок мы получим результат, совпадающий с результатом первого запроса, поскольку соединения будут выполняться в том порядке, в каком они записаны.
    Эквисоединения
    Соединения, которые мы рассмотрели ранее и которые преобладают в примерах данного
    учебника, называются соединениями по предикату.
    Синтаксис этого вида соединения такой:
    1. Таблица_1 <тип соединения>
    JOIN
    Таблица_2
    ON
    <предикат> где тип соединения := [INNER] | [OUTER]{LEFT | RIGHT | FULL}
    Эти соединения являются наиболее общими, т.к. в качестве предиката может быть использовано любое логическое выражение. Именно по этой причине все диалекты поддерживают этот вид соединения.
    Частным, но часто используемым соединением является эквисоединение - случай, когда предикат представляет собой равенство значений в столбцах соединяемых таблиц.
    При этом соединяемые столбцы зачастую имеют одинаковые имена, поскольку в соединении участвуют таблицы, связанные внешним ключом.
    Впрочем, последнее не суть важно, т.к. мы можем переименовать столбцы, если это нам потребуется.
    Так вот для этого частного случая соединения - эквисоединения по столбцам с одинаковыми именами - имеются отдельные синтаксические формы соединения: естественное соединение и соединение, использующее имена столбцов.
    Естественное соединение
    1. Таблица_1
    NATURAL
    <тип соединения>
    JOIN
    Таблица_2
    Предикат здесь не нужен, т.к. он подразумевается, а именно попарное равенство всех столбцов с одинаковыми именами в обеих таблицах. Например, если у обеих соединяемых таблиц есть столбцы a и b, то естественное соединение
    1. Таблица_1
    NATURAL
    INNER
    JOIN
    Таблица_2 будет эквивалентно такому соединению по предикату:
    1. Таблица_1
    INNER
    JOIN
    Таблица_2
    ON
    Таблица_1.a =
    Таблица_2.a
    AND
    Таблица_1.b = Таблица_2.b

    Кроме того, при естественном соединении одноименные столбцы будут присутствовать в выборке в одном экземпляре. Сравните, например, результаты таких запросов (
    база данных Аэрофлот
    )
    1.
    SELECT
    *
    FROM
    Pass_in_trip
    2.
    JOIN
    Passenger
    ON
    Pass_in_trip.id_psg
    =
    Passenger.id_psg
    3.
    WHERE
    trip_no=
    1123
    ;
    trip_no
    date
    id_psg
    place
    id_psg
    name
    1123
    2003-04-05 00:00:00 3
    2a
    3
    Kevin Costner
    1123
    2003-04-08 00:00:00 1
    4c
    1
    Bruce Willis
    1123
    2003-04-08 00:00:00 6
    4b
    6
    Ray Liotta и
    1.
    SELECT
    *
    FROM
    Pass_in_trip
    2.
    NATURAL
    JOIN
    Passenger
    3.
    WHERE
    trip_no=
    1123
    ;
    id_psg
    trip_no
    date
    place
    name
    6
    1123 2003-04-08 00:00:00 4b
    Ray Liotta
    3
    1123 2003-04-05 00:00:00 2a
    Kevin Costner
    1
    1123 2003-04-08 00:00:00 4c
    Bruce Willis
    Как видно из представленных результатов, столбец id_psg, по которому выполняется соединение, не повторяется для естественного соединения.
    Из СУБД, доступных на сайте sql-ex.ru
    , только SQL Server не поддерживает естественное соединение. Если вы хотите поработать с естественным соединением практически, выберите в консоли
    PostgreSQL или MySQL.
    Если требуется выполнить эквисоединение не по всем столбцам с совпадающими именами, а только по их части, тогда мы можем использовать соединение USING:

    1. Таблица_1 <тип соединения>
    JOIN
    Таблица_2
    USING
    (
    <список столбцов>
    )
    Список столбцов содержит те столбцы, по которым выполняется эквисоединение. Соответственно, в этом списке могут присутствовать только те из столбцов, имена которых совпадают в обеих соединяемых таблицах.
    Сравните результаты следующих запросов (предикат в предложении WHERE использован лишь для сокращения размера выборки).
    Соединение строк из таблиц Income и Outcome по равенству значений в столбце date (
    база данных Фирма вторсырья
    )
    1.
    SELECT
    *
    FROM
    Income
    JOIN
    Outcome
    USING
    (
    date
    )
    2.
    WHERE
    MONTH
    (
    date
    )
    >=
    4
    ;
    date
    code
    point
    inc
    code
    point
    out
    2001-04-13
    00:00:00
    6 1
    5000.00 7
    1 4490.00
    2001-04-13
    00:00:00
    10 1
    5000.00 7
    1 4490.00
    2001-05-11
    00:00:00
    7 1
    4500.00 9
    1 2530.00
    2001-09-13
    00:00:00
    12 3
    1350.00 16 3
    1200.00
    2001-09-13
    00:00:00
    13 3
    1750.00 16 3
    1200.00
    2001-09-13
    00:00:00
    12 3
    1350.00 17 3
    1500.00
    2001-09-13
    00:00:00
    13 3
    1750.00 17 3
    1500.00
    Соединение строк из таблиц Income и Outcome по равенству значений в столбцах date и point
    1.
    SELECT
    *
    FROM
    Income
    JOIN
    Outcome
    USING
    (
    date, point
    )

    2.
    WHERE
    MONTH
    (
    date
    )
    >=
    4
    ;
    point
    date
    code
    inc
    code
    out
    1
    2001-04-13 00:00:00 6
    5000.00 7
    4490.00
    1
    2001-04-13 00:00:00 10 5000.00 7
    4490.00
    1
    2001-05-11 00:00:00 7
    4500.00 9
    2530.00
    3
    2001-09-13 00:00:00 12 1350.00 16 1200.00
    3
    2001-09-13 00:00:00 13 1750.00 16 1200.00
    3
    2001-09-13 00:00:00 12 1350.00 17 1500.00
    3
    2001-09-13 00:00:00 13 1750.00 17 1500.00
    Соединение строк из таблиц Income и Outcome по равенству значений в столбцах date, point и code
    1.
    SELECT
    *
    FROM
    Income
    JOIN
    Outcome
    USING
    (
    date, point, code
    )
    ; не возвращает строк.
    Это соединение по всем столбцам с совпадающими именами эквивалентно естественному соединению
    1.
    SELECT
    *
    FROM
    Income
    NATURAL
    JOIN
    Outcome;
    Чтобы продемонстрировать вывод в последнем варианте, воспользуемся левым соединением
    1.
    SELECT
    *
    FROM
    Income
    LEFT
    JOIN
    Outcome
    USING
    (
    date, point, code
    )
    2.
    WHERE
    MONTH
    (
    date
    )
    >=
    4
    ;
    code
    point
    date
    inc
    out
    6
    1 2001-04-13 00:00:00 5000.00 NULL
    7
    1 2001-05-11 00:00:00 4500.00
    NULL

    10
    1 2001-04-13 00:00:00 5000.00 NULL
    12
    3 2001-09-13 00:00:00 1350.00 NULL
    13
    3 2001-09-13 00:00:00 1750.00 NULL
    FULL JOIN
    и MySQL
    Полное внешнее соединение (
    FULL
    JOIN
    ) не поддерживается в MySQL. Можно считать, что это –
    «избыточная» операция, т.к. она представляется через объединение левого и правого внешних соединений.
    Например, запрос
    1.
    --(1)--
    2.
    SELECT
    *
    FROM
    Income_o I
    FULL
    JOIN
    Outcome_o O
    3.
    ON
    I.point = O.point
    AND
    I.date = O.date; который на каждый рабочий день по каждому пункту выводит в одну строку приход и расход (
    схема «Вторсырье»
    ), можно переписать в виде:
    1.
    --(2)--
    2.
    SELECT
    *
    FROM
    Income_o I
    LEFT
    JOIN
    Outcome_o O
    3.
    ON
    I.point = O.point
    AND
    I.date = O.date
    4.
    UNION
    5.
    SELECT
    *
    FROM
    Income_o I
    RIGHT
    JOIN
    Outcome_o O
    6.
    ON
    I.point = O.point
    AND
    I.date = O.date;
    С логической точки зрения эти запросы эквивалентны; оба они выводят как дни, когда был и приход, и расход, так и дни, когда отсутствовала одна из операций (отсутствующие значения заменяются NULL). Однако с точки зрения производительности второй запрос проигрывает первому вдвое по оценке стоимости плана. Это связано с тем, что операция UNION приводит к
    выполнению сортировки, которая отсутствует в плане первого запроса.
    Сортировка же необходима для процедуры исключения дубликатов, т.к. левое и правое соединения оба содержат строки, соответствующие внутреннему соединению, т.е. случаю, когда есть как приход, так и расход. Поэтому, если вместо UNION написать UNION ALL, то такие строки будут присутствовать в результирующем наборе в двух экземплярах.
    Тем не менее, чтобы получить план, близкий по стоимости FULL JOIN, нужно избавиться от сортировки. Например, использовать UNION ALL, но в одном из объединяемых запросов исключить строки, соответствующие внутреннему соединению:
    1.
    --(3)--
    2.
    SELECT
    *
    FROM
    Income_o I
    LEFT
    JOIN
    Outcome_o O
    3.
    ON
    I.point = O.point
    AND
    I.date = O.date
    4.
    UNION
    ALL
    5.
    SELECT
    NULL
    ,
    NULL
    ,
    NULL
    ,*
    FROM
    Outcome_o O
    6.
    WHERE
    NOT
    EXISTS
    (
    SELECT
    1
    FROM
    Income_o I
    7.
    WHERE
    I.point = O.point
    AND
    I.date =
    O.date
    )
    ;
    Обратите внимание, что заведомо отсутствующие значения, которые появлялись в правом соединении решения (2), здесь формируются явным заданием NULL-значений. Если по каким-то причинам, явное задание NULL вместо соединения вам не подходит, можно оставить соединение, но это даст более дорогой план, хотя и он будет дешевле плана с сортировкой (2):
    1.
    SELECT
    *
    FROM
    Income_o I
    LEFT
    JOIN
    Outcome_o O
    2.
    ON
    I.point = O.point
    AND
    I.date = O.date
    3.
    UNION
    ALL
    4.
    SELECT
    *
    FROM
    Income_o I
    RIGHT
    JOIN
    Outcome_o O
    5.
    ON
    I.point = O.point
    AND
    I.date = O.date
    6.
    WHERE
    NOT
    EXISTS
    (
    SELECT
    1
    FROM
    Income_o I
    7.
    WHERE
    I.point = O.point
    AND
    I.date =
    O.date
    )
    ;

    Декартово
    произведение
    Ранее мы уже рассмотрели реализацию декартова произведения (
    пункт 5.6
    ), состоящую в перечислении через запятую табличных выражений в предложении FROM (таблицы, представления, подзапросы) при отсутствии предложения WHERE, связывающего столбцы из перечисленных источников строк. Кроме того, можно использовать еще и явную операцию соединения – CROSS JOIN, например:
    1.
    SELECT
    Laptop.model, Product.model
    2.
    FROM
    Laptop
    CROSS
    JOIN
    3. Product;
    Напомним, что при декартовом произведении каждая строка из первой таблицы соединяется с каждой строкой второй таблицы. В результате количество строк результирующего набора равно произведению количества строк операндов декартова произведения. В нашем примере таблица Laptop содержит 5 строк, а таблица Product — 16. В результате получается 5 * 16 = 80 строк. Поэтому мы не приводим здесь результат выполнения этого запроса.
    Вы можете сами проверить это утверждение, нажав ссылку "выполнить" или выполнив приведенный выше запрос с помощью консоли.
    Примечание
    :
    В чистом виде декартово произведение практически не
    используется, оно, как правило, является промежуточным
    результатом выполнения операции горизонтальной проекции
    (выборки) при наличии в операторе SELECT предложения WHERE.
    Объединение
    Для объединения запросов используется служебное слово UNION:
    1. <запрос
    1
    >
    2.
    UNION
    [
    ALL
    ]

    3. <запрос
    2
    >
    Предложение UNION приводит к появлению в результирующем наборе всех строк каждого из запросов. При этом, если определен параметр ALL, то сохраняются все дубликаты выходных строк, в противном случае в результирующем наборе присутствуют только уникальные строки. Заметим, что можно связывать вместе любое число запросов. Кроме того, с помощью скобок можно задавать порядок объединения.
    Операция объединения может быть выполнена только при выполнении следующих условий:

    количество выходных столбцов каждого из запросов должно быть одинаковым;

    выходные столбцы каждого из запросов должны быть совместимы между собой (в порядке их следования) по типам данных;

    в результирующем наборе используются имена столбцов, заданные в первом запросе;

    предложение ORDER BY применяется к результату соединения, поэтому оно может быть указано только в конце всего составного запроса.
    Пример 5.7.1
    Найти номера моделей и цены ПК и портативных компьютеров:
    1.
    SELECT
    model, price
    2.
    FROM
    PC
    3.
    UNION
    4.
    SELECT
    model, price
    5.
    FROM
    Laptop
    6.
    ORDER
    BY
    price
    DESC
    ;
    model
    Price
    1750
    1200
    1752
    1150
    1298
    1050
    1233
    980

    1321
    970
    1233
    950
    1121
    850
    1298
    700
    1232
    600
    1233
    600
    1232
    400
    1232
    350
    1260
    350
    Пример 5.7.2
    Найти тип продукции, номер модели и цену ПК и портативных
    компьютеров:
    1.
    SELECT
    Product.type, PC.model, price
    2.
    FROM
    PC
    INNER
    JOIN
    3. Product
    ON
    PC.model = Product.model
    4.
    UNION
    5.
    SELECT
    Product.type, Laptop.model, price
    6.
    FROM
    Laptop
    INNER
    JOIN
    7. Product
    ON
    Laptop.model = Product.model
    8.
    ORDER
    BY
    price
    DESC
    ;
    Type
    Model
    price
    Laptop 1750 1200
    Laptop 1752 1150
    Laptop
    1298 1050

    PC
    1233 980
    Laptop
    1321 970
    PC
    1233 950
    PC
    1121 850
    Laptop 1298 700
    PC
    1232 600
    PC
    1233 600
    PC
    1232 400
    PC
    1232 350
    PC
    1260 350
    Рассмотрим следующую задачу.
    Найти все имеющиеся единицы продукции производителя 'B'.
    Вывести номер модели и тип.
    В базе имеется один ноутбук и три ПК от производителя B, при этом все три
    ПК - одной модели.
    Если мы будем использовать объединение с помощью UNION ALL, то мы получим все эти изделия.
    1.
    SELECT
    p.model, p.type
    FROM
    pc
    JOIN
    Product p
    ON
    PC.model=p.model
    WHERE
    maker=
    'B'
    2.
    UNION
    ALL
    3.
    SELECT
    p.model, p.type
    FROM
    printer pr
    JOIN
    Product p
    ON
    pr.model=p.model
    WHERE
    maker=
    'B'
    4.
    UNION
    ALL
    5.
    SELECT
    p.model, p.type
    FROM
    laptop lp
    JOIN
    Product p
    ON
    lp.model=p.model
    WHERE
    maker=
    'B'
    ;

    model
    type
    1121
    PC
    1121
    PC
    1121
    PC
    1750
    Laptop
    А если - UNION, то из результата будут исключены дубликаты строк:
    1.
    SELECT
    p.model, p.type
    FROM
    pc
    JOIN
    Product p
    ON
    PC.model=p.model
    WHERE
    maker=
    'B'
    2.
    UNION
    3.
    SELECT
    p.model, p.type
    FROM
    printer pr
    JOIN
    Product p
    ON
    pr.model=p.model
    WHERE
    maker=
    'B'
    4.
    UNION
    5.
    SELECT
    p.model, p.type
    FROM
    laptop lp
    JOIN
    Product p
    ON
    lp.model=p.model
    WHERE
    maker=
    'B'
    ;
    1   ...   15   16   17   18   19   20   21   22   ...   47


    написать администратору сайта