Если кто не помнит, ещё в школе он играл так: https://xkcd.com/327/
А теперь вырос, и.. видать, он и зарегистрировал в Британии компанию под названием ; DROP TABLE "COMPANIES";–– LTD …
Via Bruсe Schneier
Оригинал этой записи в личном блоге.
Если кто не помнит, ещё в школе он играл так: https://xkcd.com/327/
А теперь вырос, и.. видать, он и зарегистрировал в Британии компанию под названием ; DROP TABLE "COMPANIES";–– LTD …
Via Bruсe Schneier
Оригинал этой записи в личном блоге.
Вот уже SQL-сервер под линукс готовят: https://blogs.microsoft.com/blog/2016/03/07/announcing-sql-server-on-linux/
Может, заодно и Internet Explorer под линукс сделают наконец? А то некоторые [идиотские] сайты только с IE нормально работают..
Оригинал этой записи в личном блоге.
Странно, что xml-программистов ещё не ищут..
Для тех, кто в танке: SQL (Structured Query Language) — это язык запросов. А XML и HTML — языки разметки. Они все языки, но прямого отношения к программированию не имеют. На них самих никакие программы написать невозможно, не предназначены они для этого.
Хотя многие реализации SQL-серверов поддерживают хранимые процедуры, но ни их создание, ни вызовы, ни языки, на котором эти процедуры пишутся, не стандартизованы в SQL, и если речь идёт о них, то стОит уточнить, о какой именно разновидности идёт речь.
Оригинал этой записи в личном блоге.
В одной базе данных есть две таблицы, связанные отношением 1:M. Скажем, в одной лежат посты из блога, а в другой комментарии, привязанные к постам по id.
Задача: перетащить их все в другую базу, в которой идентификаторы у постов изменятся (потому что там уже есть другие посты с теми же id, что в первой базе), но при этом сохранить привязку к ним комментариев.
С постами понятно, SELECT из первой базы и INSERT во вторую.
А вот как быть с комментариями?
Мне пока пришли в голову три идеи, но все неэффективные.
1) Для каждого поста сразу после помещения его во вторую базу, когда уже известны и старый, и новый id, найти в первой базе все комментарии по старому id и сложить их во вторую базу с новым id. Но это отдельный SQL-запрос на каждый пост, а их там дофига.
2) Сначала перенести все посты, попутно составляя табличку со старыми и новыми id, а потом переносить комментарии, заменяя id постов по этой табличке. Но если табличку держать в памяти, она получится очень нефигового размера. А в базах её создавать нельзя, правов не хватает.
3) Извлечь вместе с постами сразу все их комментарии путём LEFT OUTER JOIN. Но тогда каждый пост извлечётся целиком много раз, с каждым своим комментарием, это дикий перерасход памяти в SQL-сервере.
Как бы это сделать поэффективнее?
Оригинал этой записи в личном блоге.
Как обычно, нарисовалась из практики. Рассмотрим простенькую базу данных типа “классный журнал”. Не в смысле “очень хороший”, а в который школьникам оценки ставят.
Три таблицы. Ученики: students(stud_id, stud_name), предметы: subjects(subj_id, subj_name) и оценки, полученные учениками: scores(stud_id, subj_id, date, score). В реальности тут ещё должна быть таблица классов, привязка учеников к этим классам типа, привязка предметов к классам, но для простоты примера я эти усложнения убрал. Будем считать, что эта база для одного класса, и каждый ученик может получить не более одной оценки в день по данному предмету.
Задача: для конкретного ученика (по его stud_id) и конкретной даты вывести _полный_ список предметов и оценки, полученные этим учеником за эту дату. То есть, если оценка по данному предмету в этот день есть — вывести её, если нет — вывести прочерк, NULL или ещё что-нибудь, обозначающее отсутствие оценки.
Ограничение: это надо сделать одним запросом. Можно, конечно, сначала взять полный список предметов и для каждого послать отдельный запрос, но это крайне неэффективно. База для конкретности пусть будет MySQL.
На первый взгляд такие задачи решаются с помощью subjects LEFT OUTER JOIN scores. Но… он нормально работает только когда таблицы объединяются по уникальному полю, а в данном случае в таблице scores поле subj_id не уникально. Соответственно, если там есть хоть одна запись для данного предмета (длялюбого ученика за любую дату), то в результате JOIN’а не окажется строк, где score, stud_id и date есть NULL. А если потом к этому результату применить ещё WHERE stud_id=… AND date=…, то даже те строки, где NULLы могли случайно оказаться, будут удалены, поскольку не соответствуют условию.
Вот и как это сделать? Подсказка: это возможно. И на самом деле, оказалось очень просто.
Оригинал этой записи в личном блоге.
Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме.
Имеется скрипт. Выполняет довольно простую работу:
соединяется с базой данных (Informix, как водится), создаёт временную таблицу, заполняет её данными из других таблиц.
Потом запускает на эту таблицу SELECT COUNT(*)
, ну так, чисто для отчётности, и выводит это количество в лог.
Потом создаёт в таблице индекс для ускорения последующих операций и запускает UPDATE STATISTICS
, чтоб индекс заработал.
Затем удаляет из таблицы некоторые ненужные данные посредством DELETE
, опять запускает SELECT COUNT(*)
и… внезапно случается облом:
execute failed: SQL: -710: Table has been dropped, altered or renamed.
Сначала подумали, что в скрипте что-то не то. Но его не меняли уже больше года, а ошибка появилась лишь несколько месяцев назад, до этого всё нормально работало.
Потом появилась идея, что скрипт использует неуникальное имя таблицы, и какой-то другой процесс её случайно удаляет, приняв за свою. Но таблица-то временная, она из других сессий вообще не видна. Но на всякий случай попробовали переименовать. Фиг там, падает в том же месте.
Потом подумали, что может это на сервере регулярно проводятся какие-то работы как раз когда скрипт по крону запускается. Но нифига, он падает в одном и том же месте в любое время дня и ночи. Вот пока идёт первоначальный INSERT
, таблица есть, первый SELECT
— есть, DELETE
— всё ещё есть, а на следующем SELECT
‘е через пару секунд — уже нет. Мистика какая-то..
Чудес не бывает. Через пару недель исследований грабли были найдены. Они оказались очень красивыми. Естественно, я не смог мимо них пройти, не наступив. Попробуйте угадать, что это было.
Подсказка: скрипт использует перловый DBI. То есть, запросы сначала подготавливаются посредством prepare(), а потом уже выполняются вызовом execute(). Запрос про SELECT COUNT(*)
запускается несколько раз, после каждого изменения в таблице, но он всегда одинаковый, поэтому для него применяется prepare_cached(), это немножко ускоряет работу.
Оригинал этой записи в личном блоге.
Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме.
Для DBA её тоже можно применять. На самом начальном этапе, потому что если кандидат не смог навскидку ответить, то он никакой не DBA, а чмо какое-то. А вот программист такого может сразу не знать, но придумать должен, если умный.
Итак. Есть табличка foo, в которой содержатся данные об игроках по дням в следующем виде: date, user_id, nickname и кучка других данных за указанный день, которые нас не интересуют. Игрок идентифицируется по user_id и участвует в игре под псевдонимом nickname. В теории nickname у игрока меняться не должен, а на практике может, поэтому в разные дни у одного user_id может быть разный nickname. Для каждого user_id за каждую дату может быть не более одной записи, потому что nickname в течение дня меняться не может, к счастью.
Задача: выбрать одним запросом (можно с подзапросами) полный список всех user_id и для каждого из них один-единственный nickname – тот, под которым игрок играл последний раз.
Классический ответ под катом, но если кто знает другие варианты – welcome.
( Read the rest of this entry » )Оригинал этой записи. Комментировать можно тут или там.
Пишем простейшую хранимую процедуру, которая умеет иногда генерировать исключения:
CREATE PROCEDURE tmp_proc ( p_foo int ) IF p_foo = 0 THEN RAISE EXCEPTION -746, 0, "tmp_proc-raised exception"; END IF END PROCEDURE;
Вызываем её из перла посредством DBD::Informix:
“EXECUTE PROCEDURE tmp_proc (p_foo = ?)”, prepare, execute..
Как ожидалось, получаем
DBI::err = '-746', DBI::errstr='SQL: -746: tmp_proc-raised exception'
Преобразуем процедуру в функцию:
CREATE PROCEDURE tmp_func ( p_foo int ) RETURNING int; DEFINE foo int; IF p_foo = 0 THEN RAISE EXCEPTION -746, 0, "tmp_func-raised exception"; END IF LET foo = p_foo+1; RETURN foo; END PROCEDURE;
“EXECUTE PROCEDURE tmp_func (p_foo = ?)”, prepare, execute..
И фиг там. execute успешно отрабатывает, возвращает 0E0, DBI::err и DBI::errstr совершенно undefined, всё прекрасно, никаких ошибок.
А вот если потом вызвать fetchrow_array(), то только тогда
DBI::err = '-746', DBI::errstr='SQL: -746: tmp_func-raised exception'.
Что совсем плохо, если какой-нибудь fetch() совсем не вызывать, то функция, похоже, совсем не запускается. Либо результаты её работы откатываются. Потому что изначально я на это наткнулся на функции, которая проверяет данные, добавляет строку в таблицу и возвращает ID от добавленной строки. Вот если этот ID потом не считать с помощью fetch, то данные в таблицу вообще не попадают.
Я даже не понимаю, куда тут копать..
Оригинал этой записи. Комментировать можно тут или там.
Выбираем данные из некоторой таблички:
SELECT foo,bar FROM table1 WHERE условия;
Всё отлично, данные выбираются. Но мне нужны не все, а только те, которые не встречаются в другой табличке.Проверяю парочку:
SELECT * FROM table2 WHERE foo='foo1';
Ура, ничего не находится. Значит, они должны появиться в итоговой выборке. Пишем окончательный запрос:
SELECT foo,bar FROM table1 WHERE условия AND foo NOT IN (SELECT foo FROM table2);
И.. получаем индейскую национальную избу. 0 rows, и всё тут. Данных в table2 совершенно точно нет, но и из table1 они выбираться перестали.
Профессиональные DBA уже, наверное, догадались, в чём дело, но поскольку я не, мне на это понадобилось минут 10..
Оригинал этой записи. Комментировать можно тут или там.
Есть табличка tbl. В ней есть записи нескольких типов, различаются по значению поля type. Кроме того, есть поле disporder (числовое, определяет, в каком порядке записи должны показываться пользователю) и поле prio (приоритет, может быть ‘Y’ или ‘N’). Для записей типа 1 prio может быть равен ‘Y’ максимум у одной записи в таблице, а может вообще у всех быть ‘N’.
Задача: одним запросом вывести из таблицы поле title (и только его) для всех записей типа 2, а также для первых трёх (в порядке prio+disporder) записей типа 1, причём выборка должна быть отсортирована в целом по disporder, но если в выборке встречается запись типа 1, у которой prio=’Y', то она должна оказаться на первом месте.
Пользоваться можно вашим любимым диалектом SQL, а в моём для полного счастья не допускается FIRST и ORDER BY в SELECT’ах, объединенных в UNION. UNION, понятное дело, можно сортировать только целиком и только по полям, присутствующим во всех входящих в него SELECT’ах.
Оригинал этой записи. Комментировать можно тут или там.
Запрос
SELECT "http://domain.tld/dir/" || tbl.image FROM tbl
не работает: Found a quote for which there is no matching quote.
Если кавычки заменить на одиночные, то работает. И если убрать один слэш после http:, то тоже работает, только результат получается не тот, который нужен. (Это Informix, если что).
Не работает из Aqua Data Studio. Есть такой графический клиент на джаве, поддерживающий кучу разных СУБД. А из консольного клиента прекрасно работает во всех вариантах.
Вопрос: кто виноват (Aqua, jdbc, …), и как им удалось этого добиться?
Оригинал этой записи. Комментировать можно тут или там.
Помогите, не дайте умереть в неведении!
Как на ANSI SQL (или на Informix-диалекте) пишется _эффективный_ запрос на апдейт многих полей в одной таблице данными из другой таблицы?
Так работает, но по-моему, это сильно неэффективный метод:
UPDATE t1 SET
c1 = (SELECT c1 FROM t2 WHERE t2.id=t1.id),
c2 = (SELECT c2 FROM t2 WHERE t2.id=t1.id),
c3 = (SELECT c3 FROM t2 WHERE t2.id=t1.id),
...
WHERE id IN (SELECT id FROM t2);
Когда поле одно, это ещё ничего, а когда их десяток, это как-то некрасиво выглядит.
Upd: на информиксовском диалекте это пишется так:
UPDATE t1 SET (c1,c2,c3)=
((SELECT c1,c2,c3 FROM t2 WHERE t2.id=t1.id))
Двойные скобки у вложенного селекта существены.
Оригинал этой записи в личном блоге.
unload to 'tmp_file' delimiter '|'
select
...
case when u.name is null then "|" when u.name like "% %" then REPLACE(TRIM(u.name)," ", "|") else TRIM(u.name) || "|" end as first_name_surname
...
sed -e 's/\\|/|/' -e 's/\\|/ /g' tmp_file>file