dil: (Default)
Friday, January 6th, 2017 01:32 pm

Если кто не помнит, ещё в школе он играл так: https://xkcd.com/327/

А теперь вырос, и.. видать, он и зарегистрировал в Британии компанию под названием ; DROP TABLE "COMPANIES";–– LTD

Via Bruсe Schneier

Оригинал этой записи в личном блоге.

Tags:
dil: (Default)
Wednesday, September 28th, 2016 03:55 pm

Вот уже SQL-сервер под линукс готовят: https://blogs.microsoft.com/blog/2016/03/07/announcing-sql-server-on-linux/

Может, заодно и Internet Explorer под линукс сделают наконец? А то некоторые [идиотские] сайты только с IE нормально работают..

Оригинал этой записи в личном блоге.

dil: (Default)
Friday, September 18th, 2015 08:09 pm

Странно, что xml-программистов ещё не ищут..

Для тех, кто в танке: SQL (Structured Query Language) — это язык запросов. А XML и HTML — языки разметки. Они все языки, но прямого отношения к программированию не имеют. На них самих никакие программы написать невозможно, не предназначены они для этого.
Хотя многие реализации SQL-серверов поддерживают хранимые процедуры, но ни их создание, ни вызовы, ни языки, на котором эти процедуры пишутся, не стандартизованы в SQL, и если речь идёт о них, то стОит уточнить, о какой именно разновидности идёт речь.

Оригинал этой записи в личном блоге.

dil: (Default)
Saturday, August 16th, 2014 12:17 pm

В одной базе данных есть две таблицы, связанные отношением 1:M. Скажем, в одной лежат посты из блога, а в другой комментарии, привязанные к постам по id.
Задача: перетащить их все в другую базу, в которой идентификаторы у постов изменятся (потому что там уже есть другие посты с теми же id, что в первой базе), но при этом сохранить привязку к ним комментариев.

С постами понятно, SELECT из первой базы и INSERT во вторую.

А вот как быть с комментариями?
Мне пока пришли в голову три идеи, но все неэффективные.

1) Для каждого поста сразу после помещения его во вторую базу, когда уже известны и старый, и новый id, найти в первой базе все комментарии по старому id и сложить их во вторую базу с новым id. Но это отдельный SQL-запрос на каждый пост, а их там дофига.

2) Сначала перенести все посты, попутно составляя табличку со старыми и новыми id, а потом переносить комментарии, заменяя id постов по этой табличке. Но если табличку держать в памяти, она получится очень нефигового размера. А в базах её создавать нельзя, правов не хватает.

3) Извлечь вместе с постами сразу все их комментарии путём LEFT OUTER JOIN. Но тогда каждый пост извлечётся целиком много раз, с каждым своим комментарием, это дикий перерасход памяти в SQL-сервере.

Как бы это сделать поэффективнее?

Оригинал этой записи в личном блоге.

dil: (Default)
Monday, January 7th, 2013 08:37 am

Как обычно, нарисовалась из практики. Рассмотрим простенькую базу данных типа “классный журнал”. Не в смысле “очень хороший”, а в который школьникам оценки ставят.

Три таблицы. Ученики: 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 в любой форме и любом объёме.

dil: (Default)
Wednesday, December 5th, 2012 09:35 pm

Имеется скрипт. Выполняет довольно простую работу:
соединяется с базой данных (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 в любой форме и любом объёме.

dil: (Default)
Wednesday, September 15th, 2010 06:20 pm

Для 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 » )

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Wednesday, March 24th, 2010 06:51 pm

Пишем простейшую хранимую процедуру, которая умеет иногда генерировать исключения:

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, то данные в таблицу вообще не попадают.

Я даже не понимаю, куда тут копать..

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Friday, February 19th, 2010 05:16 pm

Выбираем данные из некоторой таблички:

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..

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Tuesday, January 26th, 2010 04:03 pm

Есть табличка 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’ах.

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Tuesday, January 26th, 2010 11:55 am

Запрос
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, …), и как им удалось этого добиться?

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Thursday, May 28th, 2009 12:06 pm

Помогите, не дайте умереть в неведении!

Как на 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))

Двойные скобки у вложенного селекта существены.

Оригинал этой записи в личном блоге.

dil: (Default)
Monday, March 30th, 2009 01:28 pm
ну раз уж он сегодня случился, то:
не ленитесь в операторах SELECT и INSERT явно перечислить имена полей. Даже если их много.

Это сэкономит вам много времени и нервов.
Когда какому-нибудь доброму человеку придёт в голову ночью, тихонько, никому не говоря, поменять структуру таблицы.
Tags:
dil: (Default)
Tuesday, January 20th, 2009 11:22 am
а вот кусочек SQLного запроса:

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
...


вы заметили, что delimiter тоже '|', да? но это ещё ничего. потом на полученный файл запускается
sed -e 's/\\|/|/' -e 's/\\|/ /g' tmp_file>file

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

Upd: и засеките, сколько времени у вас уйдёт на решение этой задачи
dil: (Default)
Thursday, October 2nd, 2008 11:17 am
Я раньше думал, что Оракл умный. И сам умеет оптимизировать выполнение запросов. А тут наткнулся на ситуацию, когда простейший
SELECT count(*) FROM однатаблица WHERE поле = 'foo' AND дата_добавления >= SYSDATE-30/1440
выполнялся по 15 минут, пока в запрос явным образом не добавили /*+ INDEX_JOIN(BAZ) */

Что я делаю неправильно?
Tags:
dil: (Default)
Thursday, June 26th, 2008 11:25 am
(очень задумчиво) а что, в PHP'шном SQL API ещё не реализовали placeholders и binding?

Типа, как это в перловом DBD сделано: при вызове prepare() в SQLном операторе пишутся вопросики, а реальные значения передаются при вызове execute().
Tags:
dil: (Default)
Tuesday, June 17th, 2008 05:19 pm
"Многие не представляют себе компьютер без Microsoft Office, и мало кто знает что ему есть практически не уступающая по функционалу и удобству альтернатива которая носит название OpenOffice. И скорее всего принципиальной разницы вы не сможете найти между ними - OpenOffice прекрасно заменяет и работает с документами Microsoft Word, Excel, Power Point."


"MS SQL заменяется бесплатной MySQL, если Ваш админ не знает."
Ну хорошо хоть, что не Оракл.

"Большим сюрпризом оказалось то что сервер под никсы пришлось брать мощнее, чем под ПО от MS, потому что как справедливо заметил Артем, ПО от MS всем знакомо. Если что-то произойдет с MS SQL или Server 2008 Enterprise, то пофиксят. Если же с никсами что-то будет - проще сразу бОльшую мощность задействовать..."
Как мощность сервера соотносится с знакомостью ПО - у меня в голове не укладывается.

"При использовании бесплатного ПО все риски лежат на компании, при платном ПО - риски распределяются между пользователем и производителем."
Ага, щаз.

"То что вы цитируете по поводу лицензий снимает финасовую отвественость с производителя ПО, но попробуйте представить, что он отказывается исправлять свои ошибки"
Попробовал. Легко представил.


Остальные перлы: http://www.e-xecutive.ru/forum/forum37/topic4415/messages/

via [livejournal.com profile] nikulina
originally via [livejournal.com profile] ocume
dil: (Default)
Friday, May 23rd, 2008 02:36 pm
Можно ли считать SQL языком программирования?
А Пролог?

Upd: чтоб было понятнее, откуда возник вопрос. Они оба не являются алгоритмическими языками, в том смысле, что не содержат алгоритма получения результата. Они содержат только критерии, которым этот результат должен удовлетворять. Можно ли считать их при этом языками программирования?
dil: (Default)
Friday, May 2nd, 2008 04:29 pm
Я даже не могу назвать это SQL injection. Это просто заходи кто хочешь, бери, что хочешь. Или клади.

http://thedailywtf.com/Articles/Oklahoma-Leaks-Tens-of-Thousands-of-Social-Security-Numbers,-Other-Sensitive-Data.aspx

via [livejournal.com profile] motto и [livejournal.com profile] object.
Tags:
dil: (Default)
Thursday, October 13th, 2005 12:21 pm
Ну вылитый паскаль.

Кроме того много думал о менталитете авторов Оракла. Он у них получился весь такой логичный-логичный. Но в некоторых местах всё-таки очень ненатуральный..

Вот, например, там нет автоинкрементируемых полей. Вместо них есть sequence, из которых при необходимости можно извлечь очередное значение и засунуть его в нужное поле. При добавлении записи это можно сделать автоматически, например, триггером.
Но вот как клиент может узнать, какое именно значение попало в свежедобавленную запись? Посмотреть в sequence можно, но если в это время кто-нибудь еще вставлял запись в ту же таблицу, то sequence мог уже поменяться.
[livejournal.com profile] cybernatic_cat предложил весь процесс вставки завернуть в хранимую функцию, которая будет сама извлекать очередное значение из sequence, и потом его возвращать.
Да, это будет надёжно работать, но.. как-то оно совсем ненатурально по сравнению с простым и логичным MySQLным SELECT LAST_INSERT_ID() или MSSQLным SELECT @@IDENTITY.

Или я просто не умею правильно готовить этих кошек?