Виталий Яковенко: Ну так я же черным по белому написал, что select перенаправляет в default когда канал заблокирован на запись, то есть реализует неблокируемую запись. К закрытому каналу это никакого отношения не имеет. При попытке писать в закрытый канал - получите panic'у, пусть хоть там у Вас 20 default'ов.
И так хорошее решение, куда уж проще. Можно и в int хранить, а можно и в строке, но зачем, если time.Time удобнее?
Если Вас напрягает писать if'ы каждый раз, ну так сделает функцию-обертку, которая получает на вход pq.NullTime, а на выход даст все, что Вам нужно и как Вы хотите. Вот и сохраните лаконичность.
chalyshev3340: ./myloader.exe Вы как раз получаете после выполнения первой команды:
go build cgodownloader.go -o ./myloader.exe
Эта команда скомпилирует Ваш скрипт в бинарный исполняемый файл, то есть программу.
Вторая команда запустит эту программу с нужными Вам параметрами.
Suntechnic: А потом будет еще один вопрос на Тостере "Как обойтись без self::get_data() в PHP?"
Предлагаете сослаться на $this и наблюдать за вечным циклом? =)
По Вашему вопросу - измените на 17 строке нужные Вам параметры level и titleOverlap.
Касательно id удостоверьтесь, что Вы правильно скармливаете скрипту все параметры. Скопируйте вывод консоли в файл data.txt рядом со скриптом и потом попробуйте
go build cgodownloader.go -o ./myloader
./myloader (параметры по вкусу) data.txt
chalyshev3340: Ну так выложили бы хотя бы ссылку на исходник Go скрипта на github'е. А то непонятно вообще о чем речь, ибо по Вашей ссылку надо еще сделать минимум 2 перехода по ссылкам, чтобы понять какой URL и в каком Go скрипте.
Нет, это попроще. Это просто пакет, который подключаешь к своей программе на Go и можешь запускать программу в стиле myapp start|stop|restart|status, а не отдельная программа гипервизор. Любое действие можно переопределить, а также можно добавлять своих сколько хочется, загляните в examples/graceful_http, там как раз добавлено reload для изящного рестарта и test для тестирование этого самого рестарта ручками.
Dmitry Shnyrev:
Встраивание в запрос - это совсем какая-то экономия на спичках. Более того - потенциально не безопасно, если у Вас строки. В третьих - потеряете преимущества prepared statements, которые для Go очень актуальны, ведь можно подготовить запрос чуть ли не при запуске приложения и потом гонять его сколько душе угодно (так что вопрос о производительности спорный). В четвертых - не для string'ов приходится чертыхаться с конвертированием в строки, что не всегда удобно и элегантно. Так что выгода достаточно сомнительна на самом то деле.
Что касается placeholder'ов вида $1, $2 - достаточно один раз написать вспомогательную функцию которая принимает число и возвращает сколько нужно placeholder'ов и использовать ее в проекте. По лаконичности будет даже лучше.
Dmitry Shnyrev: Кому выгоднее? Базе - да, выгоднее. Приложению - нет, не выгоднее. Что легче отмасштабировать горизонтально, чтобы снять нагрузку - базу или приложение? Делаем выводы.
Своими примерами не поделюсь, так как мне пока хватало стандартного database/sql для моих задач и я не гнался за лаконичностью, а действовал по принципу "явное лучше неявного" (что, мне кажется, достаточно идеоматично для Go), дополнительно снабдив все это дело разухабистыми проверками ошибок, что в будущем сохранило много времени при дебаге. Но это совсем не то, что ищите Вы.
В принципе, ситуацию с 2мя запросами я не вижу как можно сильно не исправить к лучшему, разве что навести аккуратность и убрать лишнюю "вермишель" (пишу без проверок ошибок по Вашему примеру):
// вытягиваем статьи
articles := []Article{}
err := DB.Select(&articles, "SELECT * FROM articles")
// собираем id'шки
uids := []int{}
uidsToArticles := map[int][]Article{}
for _, a := range articles {
uids = append(uids, a.UserId)
uidsToArticles[a.UserId] = append(uidsToArticles[a.UserId], a.Id)
}
// вытягиваем пользователей
users := []User{}
err = DB.Select(&users, "SELECT * FROM users WHERE id IN (?"+strings.Repeat(",?")+")", uids...)
// раскидываем их по статьям
for _, u := range users {
for _, a := range uidsToArticles[u.Id] {
a.User = u
}
}
Dmitry Shnyrev: резюмируя: для разовых и гарантированно нечастых запросов JOIN'ы можно, для мест под нагрузкой - противопоказанно, но бывает что никуда не денешься особо.
И да, все сказанное мной относится в основном к MySQL, потому что он достаточно прямолинеен в таких вещах. Другие базы могут нашаманить чудесных оптимизаций всяких так, что и JOIN'ы шустрые и подзапросы не хуже, а то и быстрее JOIN'ов. Это уже надо вникать в особенности конкретной используемой базы.
Dmitry Shnyrev: иногда есть случаи, когда без JOIN'ов просто не обойтись или это слишком накладно. Например: у нас есть таблица комнат, каждая комната имеет один из трех цветов r/g/b и id (rooms: id, color) и есть таблица посещений этих комнат (visits: id, user_id, room_id, created_at). Задача: вывести последних 30 посещений пользователя с user_id=30 красных комнат (color=r). Этот кейс уже нельзя так просто раскатать в 2 запроса подряд.
Также, если нужно руками в базу сбегать, то лучше накатать один большой запрос и вывести себе все нужную информацию сразу, чем дергать частями. Или, если запрос выполняется гарантированно не часто (например cron'ом раз в столько-то минут), то можно его сделать сложнее, это не создаст какой-то ощутимой нагрузки на базу, а Вам ускорит дело и будет удобнее.
Для такой задачи делать JOIN, еще и LEFT JOIN - фу-фу-фу. Базу, вижу, Вам совсем не жалко!
Чем подряд 2 запроса не угодили? Сначала выбираем нужные статьи, собираем с них айдишники юзеров и выбираем потом по этим айдишникам нужных юзеров (а если у Ваш юзеры кешируются, то можно и из кеша дернуть). Для конечного юзера разница во времени отклика будет не ощутимой, а база под нагрузкой спасибо скажет за то, что не заставляете ее делать лишние накладные расходы на временные таблицы, а данные сразу по индексам дергаете простыми запросами.
И на Ваш sqlx такое дело ляжет так, как Вы хотели. Сначала заполняете map'у или slice Article'ов, потом вытаскиваете User'ов и раскидываете указатели на конкретного User'а конкретному Article'у.