Страницы

Поиск по вопросам

Показаны сообщения с ярлыком interbase. Показать все сообщения
Показаны сообщения с ярлыком interbase. Показать все сообщения

пятница, 20 марта 2020 г.

Разворачивание таблиц

#sql #interbase


Есть три таблицы


main (id, f1, f2, f3, ...)
detail (id, main_id)
sub_detail (id, param, val)


Связь main -> detail один ко многим, detail -> sub_detail один к одному (по полям id)

На выходе нужно получить таблицу с полями

main_id, f1, f2, f3, value1, value2, value3

где valueN это значение поля val из таблицы sub_detail для записи с param = N. Не
все valueN могут присутствовать в базе.

Базовый запрос выглядит так

SELECT
  m.id AS main_id,
  m.f1,
  m.f2,
  m.f3,
  s1.val AS value1,
  s2.val AS value2,
  s3.val AS value3
FROM
  main m
  LEFT JOIN detail d ON (
    d.main_id = m.id
  )
  LEFT JOIN sub_detail s1 ON (
    s1.id = d.id AND
    s1.param = 1
  )
  LEFT JOIN sub_detail s2 ON (
    s2.id = d.id AND
    s2.param = 2
  )
  LEFT JOIN sub_detail s3 ON (
    s3.id = d.id AND
    s3.param = 3
  )
WHERE
  m.id = 1;


Но он вместо одной записи возвращает столько записей, сколько есть дочерних записей
в detail.

Поставить группировку по main.id и вытаскивать MAX(s.val) я не могу, т.к. в запросе
есть еще поля f1, f2

Есть вариант на основании detail и sub_detail построить view

CREATE VIEW vw_detail (
  main_id,
  param,
  val
) AS
SELECT
  d.main_id,
  s.param,
  s.val
FROM
  detail d
  JOIN sub_detail s ON (
    d.id = s.id
  )


и вызывать ее в основном запросе

SELECT
  m.id AS main_id,
  m.f1,
  m.f2,
  m.f3,
  s1.val AS value1,
  s2.val AS value2,
  s3.val AS value3
FROM
  main m
  LEFT JOIN vw_detail s1 ON (
    s1.main_id = m.id AND
    s1.param = 1
  )
  LEFT JOIN vw_detail s2 ON (
    s2.main_id = m.id AND
    s2.param = 2
  )
  LEFT JOIN vw_detail s3 ON (
    s3.main_id = m.id AND
    s3.param = 3
  )
WHERE
  m.id = 1;


но в Interbase есть старый баг из-за которого при вызове LEFT JOIN view начинают
игнорироваться индексы и происходит полное сканирование таблицы. А писать JOIN vw_detail
нельзя, потому что некоторые (или даже все) параметры могут отсутствовать.

Пробовал писать так

CREATE VIEW vw_values (
  main_id,
  value1,
  value2,
  value3
) AS
SELECT
  m.id,
  MAX(s1.val),
  MAX(s2.val),
  MAX(s3.val),
FROM
  main m
  LEFT JOIN detail d ON (
    d.main_id = m.id
  )
  LEFT JOIN sub_detail s1 ON (
    s1.id = d.id AND
    s1.param = 1
  )
  LEFT JOIN sub_detail s2 ON (
    s2.id = d.id AND
    s2.param = 2
  )
  LEFT JOIN sub_detail s3 ON (
    s3.id = d.id AND
    s3.param = 3
  )
GROUP BY
  m.id;


и потом в основном запросе

SELECT
  m.id AS main_id,
  m.f1,
  m.f2,
  m.f3,
  v.value1,
  v.value2,
  v.value3
FROM
  main m
  JOIN vw_values v ON (
    v.main_id = m.id
  )
WHERE
  m.id = 1;


но тогда вначале идет построение VIEW с группировкой, а потом накладывание условия
v.main_id = m.id. План получается дикий.

Вариант заменить vw_values селективной процедурой тоже не подходит. Т.к. основной
запрос выполняется в другой view, а синтаксис Interbase не позволяет внутри view использовать
процедуры

P.S. Вариант перейти на Firebird не предлагать

Ссылка на DB Fiddle.
    


Ответы

Ответ 1



SELECT m.id AS main_id, m.f1, m.f2, m.f3, MAX(s1.val) AS value1, MAX(s2.val) AS value2, MAX(s3.val) AS value3 FROM main m LEFT JOIN detail d ON ( d.main_id = m.id ) LEFT JOIN sub_detail s1 ON ( s1.id = d.id AND s1.param = 1 ) LEFT JOIN sub_detail s2 ON ( s2.id = d.id AND s2.param = 2 ) LEFT JOIN sub_detail s3 ON ( s3.id = d.id AND s3.param = 3 ) WHERE m.id = 1 GROUP BY m.id, m.f1, m.f2, m.f3 fiddle

Ответ 2



Денормализовал таблицу detail и скопировал туда поле param. После чего запрос переписался в такой вид SELECT m.id AS main_id, m.f1, m.f2, m.f3, s1.val AS value1, s2.val AS value2, s3.val AS value3 FROM main m LEFT JOIN detail d1 ON ( d1.main_id = m.id AND d1.param = 1 ) LEFT JOIN sub_detail s1 ON ( s1.id = d1.id ) LEFT JOIN detail d2 ON ( d2.main_id = m.id AND d2.param = 2 ) LEFT JOIN sub_detail s2 ON ( s2.id = d2.id ) LEFT JOIN detail d3 ON ( d3.main_id = m.id AND d3.param = 3 ) LEFT JOIN sub_detail s3 ON ( s3.id = d3.id ) WHERE m.id = 1; Если кто подскажет решение без денормализации - буду рад Update Причем поля detail.main_id и detail.param должны входить в общий индекс. Если завести два отдельных, то хоть и план строится с использованием индексов, но выполнение очень долгое

воскресенье, 8 марта 2020 г.

Как высчитать скидку?

#sql #firebird #interbase


Всем привет. Есть 3 таблицы - Скидки(Discount), Клиенты(Clients) и Заказы(Orders)

Discount

id_discount count_orders percent_discount
     1           5             1,5
     2           10            2,5


Clients

id_client  Name Surname
     1     Ivan  Petrov
     2     Vasya Vasev


Orders

id   order_sum id_client


Вопрос, как посчитать общую сумму, учитывая количество заказов, который сделал клиент?
Если общее кол-во заказов 5 и выше, то идет скидка в 1.5%, если 10 и выше, то 2.5%.
В противном случае скидки нет.
Заранее спасибо
    


Ответы

Ответ 1



Firebird нет под руками, так что пишу максимально приближенный к стандартам запрос, сработает с высокой вероятностью. Вы не указали к какой конкретно сумме вы хотите применить скидку, поэтому посчитал, что следует выдать все заказы, вычислив для каждого из них сумму со скидкой, с предположением что в данный момент в заказах сумма без скидки. select O.id,O.id_client, O.order_sum-(O.order_sum/100*coalesce(D.percent_discount,0)) from Orders O left join (select id_client,max(D.count_orders) as d_cnt from (select id_client,count(1) as cnt from Orders group by id_client) as C, Discount as D where D.count_orders<=cnt group by id_client) as S on S.id_client=O.id_client left join Discount as D on D.count_orders=S.d_cnt Подзапрос C выбирает текущие количества заказов по клиентам К ним клеются все скидки с меньшим или равным количеством Подзапрос S. Берется количество для скидки для максимально подошедшего кол-ва заказов По left (т.к. скидки может и не быть) доклеиваются скидки с учетом нужного количества Таблицу clients приклеить по желанию.

Qt 5.7. Компиляция IBASE плагина в Ubuntu 16.10

#linux #ubuntu #qt #interbase


Необходимо собрать плагин QIBASE в Ubuntu 16.10 x64 для qt.5x. Мои действия описаны
здесь. 
    


Ответы

Ответ 1



Сборка была проверена на Ubuntu 16.10 и Debian jessie. Под Debian команды с sudo выполнил в терминале с root. Для сборки QT пользовался вики В /etc/apt/source.list должны быть включены исходники - deb-src. sudo apt-get build-dep qt5-default sudo apt-get install libxcb-xinerama0-dev sudo apt-get install firebird-dev Склонировал исходные коды и собрал QT5.7: cd /usr/src # или любую другую папку по-усмотрению с правами на запись git clone git://code.qt.io/qt/qt5.git cd qt5; git checkout 5.7 perl init-repository ./configure -developer-build -opensource -nomake examples -nomake tests Конфигуратор ничего не нашёл для InterBase: SQL drivers: DB2 .................. no InterBase ............ no MySQL ................ yes (plugin) OCI .................. no ODBC ................. yes (plugin) PostgreSQL ........... yes (plugin) SQLite 2 ............. no SQLite ............... yes (plugin, using bundled copy) TDS .................. yes (plugin) make -j4 # make install # т.к. developers-build, нет необходимости Собрал плагин для firebird по этому источнику export QTDIR=/usr/src/qt5 export PATH=$QTDIR/qtbase/bin:$PATH cd $QTDIR/qtbase/src/plugins/sqldrivers/ibase # для обхода ошибки: /usr/bin/ld.gold: error: cannot find -lgds sudo ln -s /usr/lib/x86_64-linux-gnu/libfbclient.so /usr/lib/libgds.so qmake "INCLUDEPATH+=/usr/include" "LIBS+=-L/usr/lib/x86_64-linux-gnu -lfbclient" ibase.pro make Следующая ошибка уже известна и ожидает лечения баг /usr/include/c++/6/cstdlib:75:25: fatal error: stdlib.h: No such file or directory #include_next ^ Поправляем сгенерированный Makefile и повторяем make: INCPATH = -I. -isystem /usr/include --> меняем на INCPATH = -I. -I/usr/include Вывод: rm -f libqsqlibase.so g++ -Wl,--no-undefined -fuse-ld=gold -Wl,--enable-new-dtags -Wl,-rpath,/usr/src/qt5/qtbase/lib -shared -o libqsqlibase.so .obj/main.o .obj/qsql_ibase.o .obj/moc_qsql_ibase_p.o -L/usr/lib/x86_64-linux-gnu -lfbclient -lgds -L/usr/src/qt5/qtbase/lib -lQt5Sql -lQt5Core -lpthread mv -f libqsqlibase.so ../../../../plugins/sqldrivers/ -rwxr-xr-x 1 db src 1241008 May 1 23:55 ../../../../plugins/sqldrivers/libqsqlibase.so Вроде всё. Собралось без особых ошибок. Тестовая программка показывает, что плагин ibase теперь доступен: #include #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "drivers available:" << QSqlDatabase::drivers(); QCoreApplication::exit(0); } drivers available: ("QIBASE", "QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7", "QTDS", "QTDS7")

среда, 19 июня 2019 г.

Как конкатенировать значения из 3 столбцов в 1?

Привет всем. У меня есть 2 таблицы:
Avto:
ID ID_driver 1 23 2 32
Driver
ID_driver Name Surname 23 Misha Volkov 32 Valera Petrov
Как мне запросом вывести в одно поле сразу и имя, и фамилию водителя, чтобы получить следующее при выводе в таблице Avto
ID Name_Driver 1 Misha Volkov 2 Valera Petrov
Cейчас у меня есть такой запрос, но он выводит по разным полям.
SELECT A.*, DR.NAME AS NAME, DR.SURNAME AS SURNAME FROM Avto A INNER JOIN DRIVER DR ON A.ID_Driver = DR.ID
Получается вот такое
ID ID_driver Name Surname 1 23 Misha Volkov 2 32 Valera Petrov


Ответ

ответ для interbase/firebird: select char_field1 || ' ' || char_field2 (гуглится с полпинка при правильном названии бд. )
первоначально теги были проставлены mysql/sql поэтому ответ про mysql оставлю для истории:
google://mysql concat и в принципе, когда хотите что-нибудь соединить/сложить строки (в любом языке, хоть mysql, хоть python), гуглите concat+mysql, concat python, а потом уже задавайте вопрос здесь. это будет хорошим тоном
в качестве упражнения можете исправить вот это решение:
SELECT A.*, CONCAT(DR.NAME, '%', DR.SURNAME) as driverName FROM Avto A INNER JOIN DRIVER DR ON A.ID_Driver = DR.ID
и пожалуйста, если вы о себе не думаете, подумайте о других, не надо писать названия таблиц и полей КАПСОМ. КАПСОМ следует писать операторы SQL. Иначе кто-нибудь, когда будет дебажить ваши запросы без подсветки синтаксиса (да и с ней тоже), проклянет вас черным словом.
P.S. уточняйте, пожалуйста, базу данных, поскольку тег mysql вы удалили
mysql> use db; Database changed mysql> mysql> create table driver (name text, surname text); Query OK, 0 rows affected (0.22 sec)
mysql> insert into driver values ('vanya', 'petrov'); Query OK, 1 row affected (0.03 sec)
mysql> select concat(name, surname) from driver; +-----------------------+ | concat(name, surname) | +-----------------------+ | vanyapetrov | +-----------------------+ 1 row in set (0.00 sec)
mysql> select concat(name, surname) as kek from driver; +-------------+ | kek | +-------------+ | vanyapetrov | +-------------+
автора простить можно только потому, что он не знает о существовании кучи диалектов SQL

суббота, 15 июня 2019 г.

Qt 5.7. Компиляция IBASE плагина в Ubuntu 16.10

Необходимо собрать плагин QIBASE в Ubuntu 16.10 x64 для qt.5x. Мои действия описаны здесь.


Ответ

Сборка была проверена на Ubuntu 16.10 и Debian jessie. Под Debian команды с sudo выполнил в терминале с root Для сборки QT пользовался вики
В /etc/apt/source.list должны быть включены исходники - deb-src
sudo apt-get build-dep qt5-default sudo apt-get install libxcb-xinerama0-dev sudo apt-get install firebird-dev
Склонировал с гита и собрал QT5.7:
cd /usr/src # или любую другую папку по-усмотрению с правами на запись git clone git://code.qt.io/qt/qt5.git
cd qt5; git checkout 5.7
perl init-repository
./configure -developer-build -opensource -nomake examples -nomake tests
Конфигуратор ничего не нашёл для InterBase:
SQL drivers: DB2 .................. no InterBase ............ no MySQL ................ yes (plugin) OCI .................. no ODBC ................. yes (plugin) PostgreSQL ........... yes (plugin) SQLite 2 ............. no SQLite ............... yes (plugin, using bundled copy) TDS .................. yes (plugin)
make -j4
# make install # т.к. developers-build, нет необходимости
Собрал плагин для firebird по этому источнику
export QTDIR=/usr/src/qt5 export PATH=$QTDIR/qtbase/bin:$PATH
cd $QTDIR/qtbase/src/plugins/sqldrivers/ibase
# для обхода ошибки: /usr/bin/ld.gold: error: cannot find -lgds sudo ln -s /usr/lib/x86_64-linux-gnu/libfbclient.so /usr/lib/libgds.so
qmake "INCLUDEPATH+=/usr/include" "LIBS+=-L/usr/lib/x86_64-linux-gnu -lfbclient" ibase.pro make
Следующая ошибка уже известна и ожидает лечения баг
/usr/include/c++/6/cstdlib:75:25: fatal error: stdlib.h: No such file or directory #include_next ^
Поправляем сгенерированный Makefile и повторяем make
INCPATH = -I. -isystem /usr/include --> меняем на INCPATH = -I. -I/usr/include
Вывод:
rm -f libqsqlibase.so g++ -Wl,--no-undefined -fuse-ld=gold -Wl,--enable-new-dtags -Wl,-rpath,/usr/src/qt5/qtbase/lib -shared -o libqsqlibase.so .obj/main.o .obj/qsql_ibase.o .obj/moc_qsql_ibase_p.o -L/usr/lib/x86_64-linux-gnu -lfbclient -lgds -L/usr/src/qt5/qtbase/lib -lQt5Sql -lQt5Core -lpthread mv -f libqsqlibase.so ../../../../plugins/sqldrivers/
-rwxr-xr-x 1 db src 1241008 May 1 23:55 ../../../../plugins/sqldrivers/libqsqlibase.so
Вроде всё. Собралось без особых ошибок. Тестовая программка показывает, что плагин ibase теперь доступен:
#include #include
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "drivers available:" << QSqlDatabase::drivers(); QCoreApplication::exit(0); }
drivers available: ("QIBASE", "QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7", "QTDS", "QTDS7")

четверг, 30 мая 2019 г.

Как высчитать скидку?

Всем привет. Есть 3 таблицы - Скидки(Discount), Клиенты(Clients) и Заказы(Orders)
Discount
id_discount count_orders percent_discount 1 5 1,5 2 10 2,5
Clients
id_client Name Surname 1 Ivan Petrov 2 Vasya Vasev
Orders
id order_sum id_client
Вопрос, как посчитать общую сумму, учитывая количество заказов, который сделал клиент? Если общее кол-во заказов 5 и выше, то идет скидка в 1.5%, если 10 и выше, то 2.5%. В противном случае скидки нет. Заранее спасибо


Ответ

Firebird нет под руками, так что пишу максимально приближенный к стандартам запрос, сработает с высокой вероятностью. Вы не указали к какой конкретно сумме вы хотите применить скидку, поэтому посчитал, что следует выдать все заказы, вычислив для каждого из них сумму со скидкой, с предположением что в данный момент в заказах сумма без скидки.
select O.id,O.id_client, O.order_sum-(O.order_sum/100*coalesce(D.percent_discount,0)) from Orders O left join (select id_client,max(D.count_orders) as d_cnt from (select id_client,count(1) as cnt from Orders group by id_client) as C, Discount as D where D.count_orders<=cnt group by id_client) as S on S.id_client=O.id_client left join Discount as D on D.count_orders=S.d_cnt
Подзапрос C выбирает текущие количества заказов по клиентам К ним клеются все скидки с меньшим или равным количеством Подзапрос S. Берется количество для скидки для максимально подошедшего кол-ва заказов По left (т.к. скидки может и не быть) доклеиваются скидки с учетом нужного количества
Таблицу clients приклеить по желанию.