[info]kelionj


Блог обо всем и ни о чем


Сравнение строк версий в базе данных
[info]kelionj

Сегодня попалась интересная задача: в таблице (пусть она называется soft) в базе есть строковый столбец "версия по" - version, в котором хранятся значения типа 7.0, 6.25, 8.4.1 и т.д. Задача была следующая - нужно было выбрать все строки, версия которых меньше либо равна заданной (реализовать нужно было на оракле). Первым делом я попробовал просто сравнивать строки:


SELECT * FROM soft WHERE version < ?;

Результат был почти тем, что требовалось: выбирались реально нужные строки, до тех пор пока я не ввел 10.0 —оказалось, что строка 7.3 больше (про сравнение строк можно почитать в интернете).
После этого, я увидел первый очевидный вариант — разделить строку точкой и отдельно сравнивать секции, но для этого нужно создавать одну storage function для разбиения строки и вторую для сравнения. Такой вариант я оставил как запасной — хотелось найти что-то проще, желательно одним сравнением. После недолгого раздумия вариант нашелся.


Решение проблемы


В оракле есть много встроенных функций, так же как и в любой другой субд. И эти функции можно использовать не по их прямому назначению — именно так я и поступил. Для сравнения я использовал функцию TO_DATE(). Итоговый вариант получился таким:


SELECT * FROM soft WHERE TO_DATE(version,'HH24.MI.SS') < TO_DATE(?,'HH24.MI.SS');

Мою задачу такая конструкция замечательно решила, т.к. в базе нет версий типа 25.61.61 — с такой конструкцией будет ошибка, т.к. часы не могут быть больше 23, а минуты и секунды больше 59. Но для таких случаев можно использовать другой формат: есть секунды после полуночи (SSSSS), день года (DDD). Сначала я думал, что мой формат не будет работать со строками типа 10 или 8.0, но оказалось что оракл сам подставляет нули в нужные секции.


Минусы данного подхода



  • Ограничение, заданное форматом (часы не могут быть больше 23). По этому нужно заранее знать, в какую секцию отвести максимальный размер,

  • Нужно заранее знать максимальное число секций (если ввести 6.2.1.15 в моем варианте будет ошибка).


Первые шаги с symfony на денвер
[info]kelionj


С тех пор, как я начал заниматься веб-программированием, я использую денвер, и еще не разу не было причины для его сноса и отдельной установки apache+php+mysql. Так вот на днях я решил начать изучение symfony framework. Посмотрев документацию, был сильно удивлен и обрадован тем что, для него существует приложение для командной строки. Дальше будет описано как в несколько шагов можно установить symfony на денвер и начать работу с ним. (ОС - Windows).


Шаг 1. Установка

Естественно, сначала нужно скачать и распаковать последнюю версию фреймворка. На момент написания это версия 1.4.1. Фреймворк поставляется в нескольких комплектациях:

1. Sandbox - это "каркас" для нового приложения - то есть распаковываем в папку проекта и начинаем работу.
2. Для установки через PEAR. Тут я тоже сильно в подробности не вдавался, потому что сразу же выбрал 3й вариант.
3. Для установки через командную строку. Я использовал этот вариант.

Фреймворк можно использовать несколькими способами - постоянно копировать для новых проектов, или хранить в одном месте его файлы и использовать их для всех проектов. Я выбрал второй путь, и распаковал все файлы в директорию Z:/usr/symfony/.


Шаг 2. Изменение параметров среды.

Для того, чтобы полноценно использовать все "фичи" symfony, необходимо добавить его директорию 'data\bin' и папку c php.exe в переменную среды path. Для этого нажимаем win+pause -> дополнительные параметры системы -> параметры среды ->системные переменные -> path. Жмем "изменить" и добавляем в конец наши директории, разделяя их знаком ";". В моем случае это "Z:\usr\symfony\data\bin" и "Z:\usr\local\php5\". Жмем ок, нажимаем win+r, вбиваем cmd - выполнить. Проверяем работоспособность - выполняем команду "symfony -V". Если все установлено верно — тогда мы должы увидеть версию symfony и рабочую директорию фреймворка:


Шаг 3. Создание и работа с проектом.

Итак, теперь мы можем использовать все прелести symfony. Посмотреть все методы командной строки можно вызвав "symfony" без параметров. Начнем с создания нового проекта.


Как и у любого инструмента, у symfony тоже есть свои недостатки. На пример, если создавать файлы проекта в директории www/, которая является WEBROOT вашего сайта, вы можете удивиться, набрав адрес: mysite.com/config/databases.yml, где в открытом виде будет выведена информация о ваших базах данных, включая логины и пароли.


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

  mysite
    --www
    --project
В папке www будут лежать файлы закгрузки, а так же html/css/js/images. А в папке project - файлы нашего проекта. Теперь начнем генерирование данных. Вызовем командную строку и перейдем в папку project. Затем вызовем
symfony generate:project mysite
И мы увидим новые папки и файлы, созданные автоматически symfony. Для корректной работы с нашей файловой структурой, необходимо изменить файл config/ProjectConfiguration.class.php. В нем нужно добавить одну строку в метод setup:
$this->setWebDir($this->getRootDir().'/../www/');
Теперь можем приступать к генерированию "приложений" нашего проекта. "Приложение" - это отдельный модуль, имеющий коренное отличие от остальной системы. Простейшими приложениями могут быть frontend и backend - пользовательская и административная часть сайта. На пример, для создания приложения frontend необходимо выполнить команду:
symfony generate:app frontend
Проверим, все ли пошло нормально. Для этого зайдем в папку www/. Там мы должны будем увидеть 2 файла: index.php и frontend_dev.php. Если их нет - значит что-то пошло не так, а именно неверно была установлена web директрия приложения. Теперь мы можем первый раз протестировать наш сайт:
http://mysite/frontend_dev.php/
Ага, видим ошибки подключения файлов. Заходим в папку www/ и во всех файлах в ней изменяем
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
на
require_once(dirname(__FILE__).'/../project/config/ProjectConfiguration.class.php');
Итак, мы видим стандартную страницу symfony с информацией о созданном проекте. Для того, чтобы применить стандартный дизайн и стандартную схему директорий для пользовательских файлов, необходимо скопировать все файлы из папки /project/web/, а так же папку sf из директории Z:\usr\symfony\data\web\ в папку /www/. Теперь, обновив страницу мы можем увидеть стандартное сообщение с использованием стандартного оформления:


Полезные ссылки


Теперь можно продолжать осваивать данный фреймворк, и вот несколько ссылок, которые могут помочь в этом нелегком деле:
http://www.symfony-project.org/jobeet/1_4/Doctrine/ru/ - пошаговое изучение фреймворка на примере создания доски объявлений о работе
http://www.slideshare.net/nperriault/30-symfony-best-practices - 30 советов для наилучшего использования symfony
http://vit228.blogspot.com/ - блог про symfony, в котором приведено решение многих мелких проблем
http://habrahabr.ru/blogs/symfony/ - раздел про симфони на хабре

Метки: ,

Выборка случайной взвешенной записи из БД
[info]kelionj
Сегодня наткнулся на следующий пост. Увидев решение данной проблемы мне захотелось решить ее по своему, потому что в приведенном коде приходиться делать слишком много вспомогательных действий — добавлять поля в базу, делать хитрые методы добавления/удаления записи.

Модель


Функция rand() в PHP возвращает случайное значение из промежутка с одинаковой вероятностью для каждого числа. Таким образом, мы можем с помощью функции rand() генерировать номер элемента в массиве, а в массив помещать "вес" элемента. Для увеличения вероятности выбора большого веса, каждый элемент в массиве повторяется "вес" раз.

Реализация


function getItem() {
	//Выбираем вес и количество записей в таблице с таким весом
	$res = mysql_query('SELECT weight, COUNT(*) count
						FROM items
						GROUP BY weight');
	$values = array();
	//Добавляем во временный массив вес count*weight раз
	//Для увеличения вероятности выбора данного веса функцией rand()
	while ($arr = mysql_fetch_assoc($res)) {
		for ($i=0;$i<$arr['count']*$arr['weight'];$i++) {
			$values[] = $arr['weight'];
		}
	}
	//Выбираем случайный вес из сгенерированного массива
	$weight = $values[rand(0,count($values)-1)];
	unset($values);
	//Выбираем случайную запись из таблицы с соответствующим весом
	$res = mysql_query('SELECT * FROM items 
			    WHERE weight = '.$weight.' 
			    ORDER BY RAND() 
		            LIMIT 0,1');
	$arr = mysql_fetch_assoc($res);
	return $weight;
}


Пример


Для тестов создал таблицу items:
+-----+--------+
| id  |	weight |
+-----+--------+
| 1   |	   0   |
| 2   |	   1   |
| 3   |	   2   |
| 4   |	   2   |
| 5   |	   2   |
| 6   |	   2   |
| 7   |	   5   |
| 8   |	   5   |
+-----+--------+


Для нее элемент с весом 5 должен быть выбран в среднем в 10 случаях из 19, (2 — 8 из 19, 1 — 1 из 19, 0 — 0 раз).
Для 19000 тестов получил результат:
5: 10065 из 19000
2: 7952 из 19000
1: 983 из 19000
Похоже на правду:)
Метки: ,

Я — Крутой php-программист:)
[info]kelionj
 
Вот так я прошел тест на сайте http://www.corp.mamba.ru/test/promo.phtml :)

Букмарклет
[info]kelionj

Многие знают, что сейчас одна из самых больших баз музыки - это сайт vkontakte.ru. Но чтобы скачивать оттуда музыку, нужно устанавливать какие-то дополнения / плагины / использовать сторонний софт и так далее. Так вот чтобы облегчить задачу, я написал маленькую программу-букмарклет, то есть код, который вставляется в адресную строку браузера и выдает ссылки на скачивание всех аудио на текущей странице. Чтобы постоянно использовать его, необходимо добавить его в закладки.

Пример работы:

Сам скриптик (UPD 01.02.2010):

javascript: function getElementsByClass(searchClass,node,tag) { var classElements = new Array();if ( node == null )node = document;if ( tag == null )tag = '*';var els = node.getElementsByTagName(tag);var elsLen = els.length;var pattern = new RegExp('(^|\\\\s)'+searchClass+'(\\\\s|$)');for (i = 0, j = 0; i < elsLen; i++) { if ( pattern.test(els[i].className) ) { classElements[j] = els[i];j++; } } return classElements; } function dldMusic(){ var els=getElementsByClass('audioRow',null,null); if (els.length==0) window.alert('%D0%98%D0%B7%D0%B2%D0%B8%D0%BD%D0%B8%D1%82%D0%B5, %D0%BD%D0%B0 %D1%8D%D1%82%D0%BE%D0%B9 %D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B5 %D0%BD%D0%B5%D1%82 %D0%BF%D0%B5%D1%81%D0%B5%D0%BD'); for (var i=0;i < els.length;i++) { var myTd=els[i].getElementsByTagName('td'); var onclick=myTd[0].innerHTML; var link='http://cs'; var elements; elements=onclick.substring(onclick.indexOf('operate(')+8); elements=elements.substring(0,elements.indexOf(')')); var elArray=elements.split(","); if (elArray.length == 3) {link = elArray[1].substring(1,elArray[1].length-1); } else {link=link+elArray[1]+".vkontakte.ru/u"+elArray[2]+"/audio/"+elArray[3].substring(1,elArray[3].length-1)+".mp3";} link="(<a href='"+link+"' title='%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C %D1%8D%D1%82%D1%83 %D0%BF%D0%B5%D1%81%D0%BD%D1%8E'>%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C</a>)"; mySpan=myTd[1].getElementsByTagName('span')[0]; mySpan.innerHTML=mySpan.innerHTML+link; } } dldMusic();
Метки: ,

Вы читаете журнал [info]kelionj