Как известно, со стороны javascript мы не можем обратиться напрямую к файлу или файлам. Единственное, можно лишь получить имя файла. Допустим, перед нами поставили задачу, организовать возможность до загрузки файла на сервер редактировать имя выбранного файла. В лоб это невозможно, но можно воспользоваться обходным путем: 1 - нужно попытаться как-то скрыть стандартный элемент обозревателя файлов, 2 - организовать вспомогательные интерфейсы, способные вызывать обозреватель файлов и получать в свое распоряжение имя файла, 3 - при сохранении файла на стороне сервера подменять имя файла на редактируемое.
Ниже представлено одно из решений данной задачки. Обратите внимание на хак, скрывающего стандартный элемент обозревателя файлов. Также в интерфейсе редактирования имени файла использован атрибут - readonly, если попытаться использовать - disabled, для предупреждения режима редактирования файла, но тогда фактически из формы будет исключен данный элемент, а значит невозможно будет отсылать редактируемое имя серверу. Решение протестировано на большинстве браузеров свежих релизов.
<?php
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'save') {
if (!empty($_FILES['file']['name'])) {
$short_name = $_REQUEST['short_name'];
$exstension = !empty($_REQUEST['exstension']) ? '.'.$_REQUEST['exstension'] : '';
$file_name = $short_name.$exstension;
$upload_dir = "./files/test";
if (!file_exists($upload_dir)) {
mkdir($upload_dir);
}
if ($_FILES['file']['error'][0] == 0) {
$upload_file = $upload_dir.'/'.$file_name;
move_uploaded_file($_FILES['file']['tmp_name'][0], $upload_file);
}
}
}
?>
<html>
<head>
<title>test</title>
</head>
<style>
#choose_btn {
width: 80px;
}
#submit_btn {
width: 80px;
}
#file_view table {
width: 300px;
}
#file_view {
background: #FFF;
border: solid #364658;
border-width: 1px;
width: 300px;
}
#file_view td {
width: 100px;
border: solid #364658;
border-width: 1px;
text-align: center;
}
.input_file {
position: absolute;
margin-left: -9000px;
-moz-opacity: 0;
filter: alpha(opacity=0);
opacity: 0.5;
}
</style>
<script src="jquery.js"></script>
<script>
$(document).on('change', '#input_file_btn', function(){
var file = {};
file.name = this.value,
win = /.*\\(.*)/,
unix = /.*\/(.*)/,
separator = /\./,
extension = /\w+/,
file.name = file.name.replace(win, "$1"),
file.name = file.name.replace(unix, "$1"),
file.name_parts = file.name.split(separator),
file.short_name = file.name_parts[0],
file.extension = file.name_parts[file.name_parts.length - 1];
file.extension = file.name_parts.length > 1 && extension.test(file.extension) ? file.extension : '';
$('#file_short_name').val(file.short_name);
$('#file_exstension').val(file.extension);
$('#file_short_name').prop('readonly', true);
}).on('click', '#edit', function(){
$('#file_short_name').prop('readonly', false);
}).on('click', '#choose_btn', function(){
$('#input_file_btn').trigger('click');
});
</script>
<body>
<form action="" method="post" enctype="multipart/form-data">
<input type="hidden" name="action" value="save"/>
<input type="file" name="file[]" id="input_file_btn" class="input_file"/>
<div>
<table id="file_view">
<thead>
<tr>
<td>mode</td>
<td>file short name</td>
<td>file exstension</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<span id="edit" style="color: blue; border-bottom: 1px dashed; cursor: pointer;">
edit
</span>
</td>
<td>
<input type="text" id="file_short_name" name="short_name" readonly />
</td>
<td>
<input type="text" id="file_exstension" name="exstension" readonly />
</td>
</tr>
</tbody>
</table>
</div>
<div>
<input id="choose_btn" type="button" value="choose file"/>
<input id="submit_btn" type="submit" value="save file"/>
</div>
</form>
</body>
</html>
PHP. Soap service. Remote Procedure Call. Разработка тестового веб-сервиса
Наш тестовый веб-сервис будет решать одну задачу: на любой входной запрос, состоящего из даты в формате - день.месяц.год - он должен возвращать дату в виде timestamp. Создадим для этого процедуру - test(). Итак, для разворачивания soap - сервиса необходимо убедиться в том, что в настройках разрешено использование soap - сервера и клиента
Далее выделяете необходимую директорию для последующих служебных файлов. В моем примере в корне хоста test.ru была выделена директория - /web_service, куда разместил следующие файлы:
.htaccess - поддержка веб - сервиса;
index.php - реализация движка веб - сервиса (класс соединения и вызываемые процедуры);
web_service.wsdl - описание нашего тестового веб - сервиса и доступа к нему;
test.php - для демонстрации работы веб - сервиса.
Ниже приведено их содержимое, думаю по ним не составит труда разобраться.
.htaccess
# Enables or disables WSDL caching feature.
php_flag soap.wsdl_cache_enabled 0
# Sets the directory name where SOAP extension will put cache files.
php_value soap.wsdl_cache_dir "/tmp"
# (time to live) Sets the number of second while cached file will be used
# instead of original one.
php_value soap.wsdl_cache_ttl 0
AddDefaultCharset utf8
index.php
<?php
ini_set('soap.wsdl_cache_enabled', 'Off');
class Connection
{
/*
Возвращает заданную дату в виде timestamp.
Формат даты - dd.mm.yyyy
*/
function test($dt) {
if (!empty($dt)) {
$dt = explode('.', $dt);
$time_stamp = mktime(0, 0, 0, $dt[1], $dt[0], $dt[2]);
return $time_stamp;
}
else {
return false;
}
}
}
try
{
$server = new SoapServer('http://192.168.126.128/web_service/web_service.wsdl?'.rand());
$server->setClass("Connection");
$server->handle();
}
catch (ExceptionFileNotFound $e)
{
echo 'Error message: ' . $e->getMessage();
}
?>
web_service.wsdl
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" name="webserviceService" targetNamespace="http://tempuri.org/" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/">
<message name="test_input">
<part name="dt" type="xs:string"/>
</message>
<message name="test_output">
<part name="request" type="xs:string"/>
</message>
<message name="null_response"/>
<portType name="Connection">
<operation name="test">
<input message="tns:test_input"/>
<output message="tns:test_output"/>
</operation>
</portType>
<binding name="bind_connect" type="tns:Connection">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="test">
<soap:operation soapAction="urn:web-service#test" style="rpc"/>
<input message="tns:test_input">
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:web-service"/>
</input>
<output message="tns:test_output">
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:web-service"/>
</output>
</operation>
</binding>
<service name="webserviceService">
<port name="webservicePort" binding="tns:bind_connect">
<soap:address location="http://192.168.126.128/web_service/index.php"/>
</port>
</service>
</definitions>
test.php
<?php
header('Content-type: text/html; charset=utf-8');
@set_time_limit(300);
try {
$client = new SoapClient("http://192.168.126.128/web_service/web_service.wsdl?".rand(),
array('trace' => 1, 'exceptions' => 1));
echo 'test('.date("d.m.Y", time()).') = '.$client->test(strval(date("d.m.Y", time())));
}
catch (SoapFault $e) {
echo "<pre>".print_r($e, true)."</pre>";
echo "<br/><br/>Error Caught";
}
?>
Стоит отметить, что при инстанцировании soap-сервера и клиентов, а также в файле описания веб-сервиса, был использован ip-адрес веб-сервера, также можно использовать и непосредственное имя хоста (test.ru).
После завершения всех работ, пробуем протестировать наш веб-сервис. На запрос
http://test.ru/web_service/test.php
мы получаем ожидаемый ответ
test(27.03.2014) = 1395903600
Что говорит о работоспособности сервиса. Всем успеха. Исходники можно скачать по следующей ссылке - download
Далее выделяете необходимую директорию для последующих служебных файлов. В моем примере в корне хоста test.ru была выделена директория - /web_service, куда разместил следующие файлы:
.htaccess - поддержка веб - сервиса;
index.php - реализация движка веб - сервиса (класс соединения и вызываемые процедуры);
web_service.wsdl - описание нашего тестового веб - сервиса и доступа к нему;
test.php - для демонстрации работы веб - сервиса.
Ниже приведено их содержимое, думаю по ним не составит труда разобраться.
.htaccess
# Enables or disables WSDL caching feature.
php_flag soap.wsdl_cache_enabled 0
# Sets the directory name where SOAP extension will put cache files.
php_value soap.wsdl_cache_dir "/tmp"
# (time to live) Sets the number of second while cached file will be used
# instead of original one.
php_value soap.wsdl_cache_ttl 0
AddDefaultCharset utf8
index.php
<?php
ini_set('soap.wsdl_cache_enabled', 'Off');
class Connection
{
/*
Возвращает заданную дату в виде timestamp.
Формат даты - dd.mm.yyyy
*/
function test($dt) {
if (!empty($dt)) {
$dt = explode('.', $dt);
$time_stamp = mktime(0, 0, 0, $dt[1], $dt[0], $dt[2]);
return $time_stamp;
}
else {
return false;
}
}
}
try
{
$server = new SoapServer('http://192.168.126.128/web_service/web_service.wsdl?'.rand());
$server->setClass("Connection");
$server->handle();
}
catch (ExceptionFileNotFound $e)
{
echo 'Error message: ' . $e->getMessage();
}
?>
web_service.wsdl
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" name="webserviceService" targetNamespace="http://tempuri.org/" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/">
<message name="test_input">
<part name="dt" type="xs:string"/>
</message>
<message name="test_output">
<part name="request" type="xs:string"/>
</message>
<message name="null_response"/>
<portType name="Connection">
<operation name="test">
<input message="tns:test_input"/>
<output message="tns:test_output"/>
</operation>
</portType>
<binding name="bind_connect" type="tns:Connection">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="test">
<soap:operation soapAction="urn:web-service#test" style="rpc"/>
<input message="tns:test_input">
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:web-service"/>
</input>
<output message="tns:test_output">
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:web-service"/>
</output>
</operation>
</binding>
<service name="webserviceService">
<port name="webservicePort" binding="tns:bind_connect">
<soap:address location="http://192.168.126.128/web_service/index.php"/>
</port>
</service>
</definitions>
test.php
<?php
header('Content-type: text/html; charset=utf-8');
@set_time_limit(300);
try {
$client = new SoapClient("http://192.168.126.128/web_service/web_service.wsdl?".rand(),
array('trace' => 1, 'exceptions' => 1));
echo 'test('.date("d.m.Y", time()).') = '.$client->test(strval(date("d.m.Y", time())));
}
catch (SoapFault $e) {
echo "<pre>".print_r($e, true)."</pre>";
echo "<br/><br/>Error Caught";
}
?>
Стоит отметить, что при инстанцировании soap-сервера и клиентов, а также в файле описания веб-сервиса, был использован ip-адрес веб-сервера, также можно использовать и непосредственное имя хоста (test.ru).
После завершения всех работ, пробуем протестировать наш веб-сервис. На запрос
http://test.ru/web_service/test.php
мы получаем ожидаемый ответ
test(27.03.2014) = 1395903600
Что говорит о работоспособности сервиса. Всем успеха. Исходники можно скачать по следующей ссылке - download
Javascript. Типовое сравнение объектов
Объявим пространство имен NS (регистрозависим)
var NS = {};
Далее объявим в нем конструктор Test и создадим на его основе экземпляр t
NS.Test = function() {
this.name = 'Test';
};
NS.t = new NS.Test();
Далее создадим объект o, используя стандартный объект Object
NS.o = {};
Для определения принадлежности того или иного экземпляра своему "классу" (в js понятие класса условно), можно воспользоваться следующими стандартными способами
NS.t.constructor == NS.Test // true
NS.t instanceof NS.Test // true
NS.o.constructor == Object // true
NS.o instanceof Object // true
Если попытаться сопоставить объект через явно объявленный конструктор с объектом Object,
то можно получить следующие результаты
NS.t.constructor == Object // false
NS.t instanceof Object // true
Первое сравнение вернуло false, это и понятно, так как этот конструктор явно объявлен и ссылается на функцию Test(), второе сравнение истинно потому, что сам конструктор является объектом, что уж там, в javascripte практически все представлено объектом, такова его реализация. В подтверждение сказанного приведу следующее сравнение
NS.Test.constructor == Object // false
NS.Test instanceof Object // true
В виде дополнения, приведу список встроенных в javascript объектов
// Массив пронумерованных элементов, также может служить стеком или очередью
Array
// Объект для булевых значений
Boolean
// Функции для работы с датой и временем
Date
// объект для представления ошибок
Error
// Ошибка при выполнении функции eval
EvalError
// Каждая функция в яваскрипт является объектом класса Function
Function
// Встроенный объект, предоставляющий константы и методы для математических вычислений
Math
// Объект для работы с числами
Number
// Базовый объект javascript
Object
// Ошибка, когда число не лежит в нужном диапазоне
RangeError
// Ошибку при ссылке на несуществующую переменную
ReferenceError
// Позволяет работать с регулярными выражениями.
RegExp
// Базовый объект для строк. Позволяет управлять текстовыми строками, форматировать их и выполнять поиск строк
String
// Ошибка при интерпретации синтаксически неверного кода
SyntaxError
// Ошибка в типе значения
TypeError
// Ошибка при некорректном URI
URIError
Небольшой пример сверки типа объекта
var stack = [];
if (stack instanceof Array) {
stack.push(1);
stack.push(2);
stack.push(3);
}
var NS = {};
Далее объявим в нем конструктор Test и создадим на его основе экземпляр t
NS.Test = function() {
this.name = 'Test';
};
NS.t = new NS.Test();
Далее создадим объект o, используя стандартный объект Object
NS.o = {};
Для определения принадлежности того или иного экземпляра своему "классу" (в js понятие класса условно), можно воспользоваться следующими стандартными способами
NS.t.constructor == NS.Test // true
NS.t instanceof NS.Test // true
NS.o.constructor == Object // true
NS.o instanceof Object // true
Если попытаться сопоставить объект через явно объявленный конструктор с объектом Object,
то можно получить следующие результаты
NS.t.constructor == Object // false
NS.t instanceof Object // true
Первое сравнение вернуло false, это и понятно, так как этот конструктор явно объявлен и ссылается на функцию Test(), второе сравнение истинно потому, что сам конструктор является объектом, что уж там, в javascripte практически все представлено объектом, такова его реализация. В подтверждение сказанного приведу следующее сравнение
NS.Test.constructor == Object // false
NS.Test instanceof Object // true
В виде дополнения, приведу список встроенных в javascript объектов
// Массив пронумерованных элементов, также может служить стеком или очередью
Array
// Объект для булевых значений
Boolean
// Функции для работы с датой и временем
Date
// объект для представления ошибок
Error
// Ошибка при выполнении функции eval
EvalError
// Каждая функция в яваскрипт является объектом класса Function
Function
// Встроенный объект, предоставляющий константы и методы для математических вычислений
Math
// Объект для работы с числами
Number
// Базовый объект javascript
Object
// Ошибка, когда число не лежит в нужном диапазоне
RangeError
// Ошибку при ссылке на несуществующую переменную
ReferenceError
// Позволяет работать с регулярными выражениями.
RegExp
// Базовый объект для строк. Позволяет управлять текстовыми строками, форматировать их и выполнять поиск строк
String
// Ошибка при интерпретации синтаксически неверного кода
SyntaxError
// Ошибка в типе значения
TypeError
// Ошибка при некорректном URI
URIError
Небольшой пример сверки типа объекта
var stack = [];
if (stack instanceof Array) {
stack.push(1);
stack.push(2);
stack.push(3);
}
Oracle. Get dates in russian language
select to_char(sysdate, 'Month', 'nls_date_language = russian') current_month from dual
Oracle. Особенности NULL значений
Как известно, в sql то или иное поле либо определено (NOT NULL), и тогда имеет определенное значение, либо не определено (NULL), и тогда с этими полями начинают обращаться более аккуратно, так как они могут вызвать неоднозначность и ошибочность в ожидаемом запросе. Для примера продемонстрирую следующий сценарий, в результатах которого (см. рисунок ниже) видно, как изменяется значение среднего арифметического значения, суммы значения, общего количества и количества строк со значащими значениями. Для сравнения поведения приведены строками ниже результаты подзапросов, где введена замещающая пустое значение на ноль функция nvl(). Видно, что при подсчетах пустые значения просто напросто не учитываются, функция nvl(), напротив, приводит к тому, что "пустые" значения включаются в расчет.
with
-- emulation of test table
virt_table( id, val ) as (
select 1, 1 from dual union all
select 2, 2 from dual union all
select 3, null from dual union all
select 4, null from dual union all
select 5, 5 from dual
),
-- test data without nulls
clear_nulls as (
select id, nvl(val, 0) val from virt_table
),
-- comparison of test data
compare( count_rows, count_val, sum_val, avg1_val, avg2_val, avg3_val, commentary ) as (
-- with nulls
select
count(*),
count(val),
sum(val),
avg(val),
sum(val) / count(val),
sum(val) / count(*),
'with nulls' commentary
from virt_table
union all
-- without nulls
select
count(*),
count(nvl(val, 0)),
sum(nvl(val, 0)),
avg(nvl(val, 0)),
sum(nvl(val, 0)) / count(nvl(val, 0)),
sum(nvl(val, 0)) / count(*),
'manual cleaning of nulls' commentary
from virt_table
union all
select
count(*),
count(val),
sum(val),
avg(val),
sum(val) / count(val),
sum(val) / count(*),
'general cleaning of nulls' commentary
from clear_nulls
)
select * from compare
Приведу для примера и поведение аналитических функций с пустыми значениями.
with
-- emulation of test table
virt_table( id, val ) as (
select 1, 1 from dual union all
select 2, 2 from dual union all
select 3, null from dual union all
select 4, null from dual union all
select 5, 5 from dual
),
-- test data without nulls
clear_nulls as (
select id, nvl(val, 0) val from virt_table
),
-- for testing analytical functions with nulls
test( count_rows, count_val, sum_val, avg_val ) as (
select
count(*) over (),
count(val) over (),
sum(val) over (),
avg(val) over ()
from virt_table
union all
select
count(*) over (),
count(val) over (),
sum(val) over (),
avg(val) over ()
from clear_nulls
)
select * from test
Для удобства результаты аналитических функций разделены красной линией (верхняя часть - с пустыми значениями, нижняя - без пустых значений). Разница налицо. Поэтому общий вывод таков, необходимо помнить о таких аномалиях и быть очень внимательными с такого рода неопределенного состояния полями. Стоит также отметить, что наличие таких полей в выборках определенных запросов также может приводить к невозможности выполнения последующих подзапросов. Вот один из классических примеров, который можно встретить при работе с иерархическими таблицами. Рассмотрим следующий запрос
with
-- emulation of test table
nodes( node, name, parent ) as (
select 1, 'root', null from dual union all
select 2, 'node2', 1 from dual union all
select 3, 'node3', 2 from dual union all
select 4, 'node4', 3 from dual union all
select 5, 'node5', 4 from dual
),
-- get path of nodes -- /root/node2/node3/...
get_nodes_path as (
select level, sys_connect_by_path ( name, '/' ) path
from nodes
connect by prior node = parent
start with parent is null
),
-- get just root
get_root as (
select node, name, parent
from nodes
where parent is null
),
-- get just leaf -- wrong variant
get_leaf_wrong as (
select node, name, parent
from nodes
where node not in (
select parent
from nodes
)
),
-- get just leaf -- correct variant
get_leaf_correct as (
select node, name, parent
from nodes
where node not in (
select parent
from nodes
where parent is not null
)
)
select * from get_nodes_path
Как видно, мы имеем дело с простым деревом. Теперь о классическом примере. Допустим, нам нужно найти все концевые узлы нашего дерева, точнее - листья. В приведенном выше запросе для этого предусмотрены два вложенных представления - get_leaf_wrong и get_leaf_correct. Вроде бы, если обратиться к представлению get_leaf_wrong то мы должны получить ожидаемые листья дерева, но здесь есть нюанс, так как в выборке
select parent
from nodes
содержится значение NULL (присущее корневому узлу), которое нельзя сравнивать с другими значениями, на выходе мы получим пустой результат. Если же исключить строку с данным неопределенным значением, что сделано в представлении - get_leaf_correct, тогда будет получен правильный ответ
Всем хорошего дня :)
with
-- emulation of test table
virt_table( id, val ) as (
select 1, 1 from dual union all
select 2, 2 from dual union all
select 3, null from dual union all
select 4, null from dual union all
select 5, 5 from dual
),
-- test data without nulls
clear_nulls as (
select id, nvl(val, 0) val from virt_table
),
-- comparison of test data
compare( count_rows, count_val, sum_val, avg1_val, avg2_val, avg3_val, commentary ) as (
-- with nulls
select
count(*),
count(val),
sum(val),
avg(val),
sum(val) / count(val),
sum(val) / count(*),
'with nulls' commentary
from virt_table
union all
-- without nulls
select
count(*),
count(nvl(val, 0)),
sum(nvl(val, 0)),
avg(nvl(val, 0)),
sum(nvl(val, 0)) / count(nvl(val, 0)),
sum(nvl(val, 0)) / count(*),
'manual cleaning of nulls' commentary
from virt_table
union all
select
count(*),
count(val),
sum(val),
avg(val),
sum(val) / count(val),
sum(val) / count(*),
'general cleaning of nulls' commentary
from clear_nulls
)
select * from compare
Приведу для примера и поведение аналитических функций с пустыми значениями.
with
-- emulation of test table
virt_table( id, val ) as (
select 1, 1 from dual union all
select 2, 2 from dual union all
select 3, null from dual union all
select 4, null from dual union all
select 5, 5 from dual
),
-- test data without nulls
clear_nulls as (
select id, nvl(val, 0) val from virt_table
),
-- for testing analytical functions with nulls
test( count_rows, count_val, sum_val, avg_val ) as (
select
count(*) over (),
count(val) over (),
sum(val) over (),
avg(val) over ()
from virt_table
union all
select
count(*) over (),
count(val) over (),
sum(val) over (),
avg(val) over ()
from clear_nulls
)
select * from test
Для удобства результаты аналитических функций разделены красной линией (верхняя часть - с пустыми значениями, нижняя - без пустых значений). Разница налицо. Поэтому общий вывод таков, необходимо помнить о таких аномалиях и быть очень внимательными с такого рода неопределенного состояния полями. Стоит также отметить, что наличие таких полей в выборках определенных запросов также может приводить к невозможности выполнения последующих подзапросов. Вот один из классических примеров, который можно встретить при работе с иерархическими таблицами. Рассмотрим следующий запрос
with
-- emulation of test table
nodes( node, name, parent ) as (
select 1, 'root', null from dual union all
select 2, 'node2', 1 from dual union all
select 3, 'node3', 2 from dual union all
select 4, 'node4', 3 from dual union all
select 5, 'node5', 4 from dual
),
-- get path of nodes -- /root/node2/node3/...
get_nodes_path as (
select level, sys_connect_by_path ( name, '/' ) path
from nodes
connect by prior node = parent
start with parent is null
),
-- get just root
get_root as (
select node, name, parent
from nodes
where parent is null
),
-- get just leaf -- wrong variant
get_leaf_wrong as (
select node, name, parent
from nodes
where node not in (
select parent
from nodes
)
),
-- get just leaf -- correct variant
get_leaf_correct as (
select node, name, parent
from nodes
where node not in (
select parent
from nodes
where parent is not null
)
)
select * from get_nodes_path
Как видно, мы имеем дело с простым деревом. Теперь о классическом примере. Допустим, нам нужно найти все концевые узлы нашего дерева, точнее - листья. В приведенном выше запросе для этого предусмотрены два вложенных представления - get_leaf_wrong и get_leaf_correct. Вроде бы, если обратиться к представлению get_leaf_wrong то мы должны получить ожидаемые листья дерева, но здесь есть нюанс, так как в выборке
select parent
from nodes
содержится значение NULL (присущее корневому узлу), которое нельзя сравнивать с другими значениями, на выходе мы получим пустой результат. Если же исключить строку с данным неопределенным значением, что сделано в представлении - get_leaf_correct, тогда будет получен правильный ответ
Всем хорошего дня :)
Oracle. Арифметика дат
В Oracle как и в других системах управления базами данных с датами можно производить арифметические действия. Стоит только помнить, что сложение и вычитание с числовыми значениями происходит по-умолчанию в контексте дней. То есть выражение вида
select sysdate + 1 from dual
на выходе даст текущую дату увеличенную ровно на 1 день, поэтому, результат разницы между датами будет исчисляться днями, это и понятно - date1 + N = date2, откуда date2 - date1 = N. Складывать даты между собой запрещено, так как это не имеет смысла. Если нужны другие контексты (месяцы, года, секунды и прочее), тогда прибегают к иному способу
select sysdate ± interval '1' year from dual
select sysdate ± interval '1' month from dual
select sysdate ± interval '1' day from dual
select sysdate ± interval '1' hour from dual
select sysdate ± interval '1' minute from dual
select sysdate ± interval '1' second from dual
select sysdate ± numtodsinterval(1, 'day')
select sysdate ± numtodsinterval(1, 'hour')
select sysdate ± numtodsinterval(1, 'minute') select sysdate ± numtodsinterval(1, 'second')
select sysdate ± numtoyminterval(1, 'month')
select sysdate ± numtoyminterval(1, 'year')
Но разность дат по-умолчанию возвращается в днях, это стоит помнить. Приведу небольшой пример
with
-- first reper date
first_date( fd ) as (
select to_date('01.01.2014 00:00:00', 'dd.mm.yyyy hh24:mi:ss') as fd
from dual
),
-- last reper date
last_date( ld ) as (
select to_date('02.01.2014 00:00:00', 'dd.mm.yyyy hh24:mi:ss') as ld
from dual
),
-- get the difference of the last and the first reper dates
get_diff_dates( diff ) as (
select abs((select ld from last_date) - (select fd from first_date))
from dual
)
select
diff days,
diff * 24 hours,
diff * 24 * 60 minutes,
diff * 24 * 60 * 60 seconds
from get_diff_dates
Вообще для манипуляции с датами в Oracle есть и другие возможности, с которыми лучше всего ознакомиться самостоятельно в документации. Всем успехов.
select sysdate + 1 from dual
на выходе даст текущую дату увеличенную ровно на 1 день, поэтому, результат разницы между датами будет исчисляться днями, это и понятно - date1 + N = date2, откуда date2 - date1 = N. Складывать даты между собой запрещено, так как это не имеет смысла. Если нужны другие контексты (месяцы, года, секунды и прочее), тогда прибегают к иному способу
select sysdate ± interval '1' year from dual
select sysdate ± interval '1' month from dual
select sysdate ± interval '1' day from dual
select sysdate ± interval '1' hour from dual
select sysdate ± interval '1' minute from dual
select sysdate ± interval '1' second from dual
select sysdate ± numtodsinterval(1, 'day')
select sysdate ± numtodsinterval(1, 'hour')
select sysdate ± numtodsinterval(1, 'minute') select sysdate ± numtodsinterval(1, 'second')
select sysdate ± numtoyminterval(1, 'month')
select sysdate ± numtoyminterval(1, 'year')
Но разность дат по-умолчанию возвращается в днях, это стоит помнить. Приведу небольшой пример
with
-- first reper date
first_date( fd ) as (
select to_date('01.01.2014 00:00:00', 'dd.mm.yyyy hh24:mi:ss') as fd
from dual
),
-- last reper date
last_date( ld ) as (
select to_date('02.01.2014 00:00:00', 'dd.mm.yyyy hh24:mi:ss') as ld
from dual
),
-- get the difference of the last and the first reper dates
get_diff_dates( diff ) as (
select abs((select ld from last_date) - (select fd from first_date))
from dual
)
select
diff days,
diff * 24 hours,
diff * 24 * 60 minutes,
diff * 24 * 60 * 60 seconds
from get_diff_dates
Вообще для манипуляции с датами в Oracle есть и другие возможности, с которыми лучше всего ознакомиться самостоятельно в документации. Всем успехов.
Oracle. внешние и внутренние соединения, декартово произведение таблиц
Общая схема соединений представлена на следующем рисунке
Для рассмотрения примеров я буду использовать внутренние представления (f1, f2) объединенные во фразе with.
Итак, левое полуоткрытое соединение. Дополнение правой таблицы отсутствующих сравниваемых значений пустыми значениями (NULL), все значения правой таблицы, которые отсутствуют в левой таблице отбрасываются
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 left outer join f2
on f1.a = f2.a
order by f1.a nulls first
данный запрос в версиях ниже 9 записывается немного иначе
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1, f2
where f1.a = f2.a (+)
order by f1.a nulls first
Правое полуоткрытое соединение. Дополнение левой таблицы отсутствующих сравниваемых значений пустыми значениями (NULL), все значения левой таблицы, которые отсутствуют в правой таблице отбрасываются
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 right outer join f2
on f1.a = f2.a
order by f1.a nulls first
на старый лад (версия ниже 9)
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1, f2
where f1.a (+) = f2.a
order by f1.a nulls first
Полное открытое соединение. Дополнение отсутствующих сравниваемых значений пустыми значениями (NULL) и в левой, и в правой таблицах. Никакие значения таблиц не теряются
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 full outer join f2
on f1.a = f2.a
order by f1.a nulls last
Внутреннее закрытое соединение. По результату схоже с естественным соединением
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 inner join f2
on f1.a = f2.a
order by f1.a nulls last
Получение декартова произведения таблиц
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1, f2
или (более предпочтительно)
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 cross join f2
если для декартова произведения таблиц наложить условия, тогда получим
внутреннее закрытое соединение таблиц
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 cross join f2
where f1.a = f2.a
Стоит отметить, что допускается в синтаксисе внутреннего inner закрытого соединения
from Table1 t1 inner join Table2 t2
on t1.id = t2.id
опускать тип соединения
from Table1 t1 join Table2 t2
on t1.id = t2.id
Также сказанное справедливо и для внешних outer соединений (полного, левого полуоткрытого и правого полуоткрытого).
full outer join -> full join
left outer join -> left join
right outer join -> right join
Отличительная особенность декартова произведения — вероятный большой объем результата, а значит более высокая нагрузка на сервер. Поэтому в соединениях необходимо использовать новый синтаксис, так как этот синтаксис требует указания сравнения значений разных столбцов друг с другом (если, конечно, это не natural inner join). Но если все же по каким-то причинам необходимо получить декартово произведение таблиц, как было упомянуто выше, правильней использовать cross join.
Для рассмотрения примеров я буду использовать внутренние представления (f1, f2) объединенные во фразе with.
Итак, левое полуоткрытое соединение. Дополнение правой таблицы отсутствующих сравниваемых значений пустыми значениями (NULL), все значения правой таблицы, которые отсутствуют в левой таблице отбрасываются
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 left outer join f2
on f1.a = f2.a
order by f1.a nulls first
данный запрос в версиях ниже 9 записывается немного иначе
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1, f2
where f1.a = f2.a (+)
order by f1.a nulls first
Правое полуоткрытое соединение. Дополнение левой таблицы отсутствующих сравниваемых значений пустыми значениями (NULL), все значения левой таблицы, которые отсутствуют в правой таблице отбрасываются
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 right outer join f2
on f1.a = f2.a
order by f1.a nulls first
на старый лад (версия ниже 9)
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1, f2
where f1.a (+) = f2.a
order by f1.a nulls first
Полное открытое соединение. Дополнение отсутствующих сравниваемых значений пустыми значениями (NULL) и в левой, и в правой таблицах. Никакие значения таблиц не теряются
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 full outer join f2
on f1.a = f2.a
order by f1.a nulls last
Внутреннее закрытое соединение. По результату схоже с естественным соединением
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 inner join f2
on f1.a = f2.a
order by f1.a nulls last
Получение декартова произведения таблиц
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1, f2
или (более предпочтительно)
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 cross join f2
если для декартова произведения таблиц наложить условия, тогда получим
внутреннее закрытое соединение таблиц
with
f1( a, b ) as (
select 1 as a, 'type 1' as b from dual
union all
select a + 1, b
from f1
where a < 10
),
f2( a, b ) as (
select 5 as a, 'type 2' as b from dual
union all
select a + 1, b
from f2
where a < 15
)
select f1.a, f1.b, f2.a, f2.b
from f1 cross join f2
where f1.a = f2.a
Стоит отметить, что допускается в синтаксисе внутреннего inner закрытого соединения
from Table1 t1 inner join Table2 t2
on t1.id = t2.id
опускать тип соединения
from Table1 t1 join Table2 t2
on t1.id = t2.id
Также сказанное справедливо и для внешних outer соединений (полного, левого полуоткрытого и правого полуоткрытого).
full outer join -> full join
left outer join -> left join
right outer join -> right join
Отличительная особенность декартова произведения — вероятный большой объем результата, а значит более высокая нагрузка на сервер. Поэтому в соединениях необходимо использовать новый синтаксис, так как этот синтаксис требует указания сравнения значений разных столбцов друг с другом (если, конечно, это не natural inner join). Но если все же по каким-то причинам необходимо получить декартово произведение таблиц, как было упомянуто выше, правильней использовать cross join.
Oracle. Аналитические функции.
Общий синтаксис для использования аналитических функций следующий
имя_функции(<аргумент>,< аргумент >, . . . )
over (<конструкция_фрагментации><конструкция_упорядочения><конструкция_окна>)
Рассмотрим основные части данного синтаксиса.
1. <конструкция_фрагментации>
Синтаксис для задания конструкции фрагментации выглядит следующим образом
partition by выражение [, выражение] [, выражение]
Данная конструкция логически разбивает результирующее множество на N групп
по критериям, задаваемым выражениями фрагментации. Аналитические функции применяются к каждой группе независимо, - для каждой новой группы они сбрасываются. Если не указать конструкцию фрагментации, все результирующее множество считается
одной группой.
2. <конструкция_упорядочения>
Конструкция упорядочения имеет следующий синтаксис
order by выражение [asc | desc] [nulls first | nulls last]
Конструкция order by задает критерий сортировки данных в каждой группе (в каждом фрагменте). Это, несомненно, влияет на результат выполнения любой аналитической функции. При наличии (или отсутствии) конструкции order by аналитические функции вычисляются по-другому. Например.
- без конструкции order by
select ename, sal, avg(sal) over ()
from emp
- с конструкцией order by
select ename, sal, avg(sal) over (order by ename)
from emp
Здесь стоит отметить следующее, на самом деле наличие конструкции order by в вызове аналитической функции добавляет стандартную конструкцию окна — RANGE UNBOUNDED PRECEDING. Это означает, что для вычисления используется набор из всех предыдущих и текущей строки в текущем фрагменте. При отсутствии order by стандартным окном является весь фрагмент. То есть по-сути предыдущий запрос будет выглядеть следующим образом
select ename, sal, avg(sal) over (order by ename RANGE UNBOUNDED PRECEDING)
from emp
3. <конструкция_окна>
Конструкция окна позволяет задать перемещающееся или жестко привязанное окно (набор)
данных в пределах группы, с которым будет работать аналитическая функция.
Например, для создания отчета, показывающего сумму зарплат текущего и двух предыдущих сотрудников отдела, можно выполнить следующий запрос
select deptno, ename, sal,
sum(sal) over (partition by deptno order by ename rows 2 preceding) sliding_total
from emp
order by deptno, ename
Можно создавать окна по двум критериям: по диапазону (RANGE) значений данных или по
смещению (ROWS) относительно текущей строки. Использование конструкции range как было сказано ранее в некоторых случаях используется неявно, RANGE UNBOUNDED PRECEDING например. Она требует брать все строки вплоть до текущей, в соответствии с порядком, задаваемым конструкцией order by. Следует помнить, что для использования окон
необходимо задавать конструкцию order by.
Окно определяется диапазоном строк, объединяемых в соответствии с заданным порядком.
Применять конструкцию range можно либо с числовыми выражениями (NUMBER), либо с выражениями, значением которого является дата (DATE). Еще одно ограничение для таких окон состоит в том, что в конструкции order by может быть только один столбец — диапазоны по природе своей одномерны. Нельзя задать диапазон в N-мерном пространстве. Пример.
Пусть необходимо выбрать зарплату каждого сотрудника и среднюю зарплату всех принятых на работу в течение 100 предыдущих дней, а также среднюю зарплату всех принятых на работу в течение 100 следующих дней. Соответствующий запрос будет выглядеть так:
select ename, hiredate, sal,
avg(sal) over (order by hiredate asc range 100 preceding) avg_sal_100_days_before,
avg(sal) over (order by hiredate desc range 100 preceding) avg_sal_100_days_after
from emp
order by hiredate desc
Помимо определения окна по диапазону (RANGE), также окна определяются и по количеству строк (ROWS). Для окон по строкам нет ограничений, присущих окнам по диапазону; данные могут быть любого типа и упорядочивать можно по любому количеству столбцов.
Например, пусть нужно вычислить среднюю зарплату для сотрудника и пяти принятых на работу до него и после него. Запрос можно записать следующим образом
select ename, hiredate, sal,
avg(sal) over (order by hiredate asc rows 5 preceding) avg_5_before,
avg(sal) over (order by hiredate desc rows 5 preceding) avg_5_after
from emp
order by hiredate
Зная как определяются окна (по диапазону или по количеству строк), рассмотрим как окончательно задаются окна. В простейшем случае, окно задается с помощью одной из трех следующих взаимоисключающих конструкций.
- UNBOUNDED PRECEDING.
Окно начинается с первой строки текущей группы и заканчивается текущей обрабатываемой строкой.
- CURRENT ROW.
Окно начинается (и заканчивается) текущей строкой.
- Числовое_выражение PRECEDING.
Окно начинается со строки за числовое_выражение строк до текущей, если оно задается по строкам, или со строки, меньшей по значению столбца, упомянутого в конструкции order by, не более чем на числовое выражение, если оно задается по диапазону.
- Числовое_выражение FOLLOWING.
Окно заканчивается (или начинается) со строки, через числовое_выражение строк после текущей, если оно задается по строкам, или со строки, большей
по значению столбца, упомянутого в конструкции order by, не более чем на числовое_выражение, если оно задается по диапазону.
Стоит отметить, что окно CURRENT ROW в простейшем виде, вероятно, никогда не используется, поскольку ограничивает применение аналитической функции одной текущей строкой, а для этого аналитические функции не нужны. В более сложном случае для окна задается также конструкция BETWEEN. В ней CURRENT ROW можно указывать в качестве начальной или конечной строки окна. Начальную и конечную строку окна в конструкции BETWEEN можно задавать с использованием любой из перечисленных выше конструкций. Например, можно задать окно так, что обрабатываемая строка не будет
последней, а окажется где-то в середине окна
select ename, hiredate,
first_value(ename) over (order by hiredate asc range between 100 preceding and 100 following),
last_value(ename) over (order by hiredate asc range between 100 preceding and 100 following)
from emp
order by hiredate asc
В данном запросе дополнительно использованы функции first_value() и last_value(), которые возвращают первое значений текущего окна и последнее значение также текущего окна, соответственно, в то время как диапазон окна ограничен слева текущая скользящая дата - 100 дней и справа к текущей скользящей дате + 100 дней, в этом и состоит смысл выражения - between 100 preceding and 100 following.
select
level,
count(*) over (order by level asc rows 2 preceding) asc_count,
count(*) over (order by level desc rows 2 preceding) desc_count
from dual
connect by level <= 10
order by level
Окно rows 2 preceding, как видно из результата запроса, содержит от 1 до 3 строк (это определяется тем, как далеко текущая строка находится от начала группы). Для первой строки группы имеем значение 1 (предыдущих строк нет). Для следующей строки в группе таких строк 2. Наконец, для третьей и далее строк значение count(*) остается постоянным, поскольку мы считаем только текущую строку и две предыдущие.
select
n,
sum(n) over (order by days range 2 preceding) n_sum,
days
from (
select
level n,
( to_date('10.01.2014', 'dd.mm.yyyy') + (level - 1) ) days
from dual
connect by level <= 10
)
order by days
В данном случае идет суммирование в пределах окна, диапазон которого составляет скользящий день и 2 предыдущих дня
select
ename,
sal,
rank() over (order by sal) rank,
dense_rank() over (order by sal) dens_rank,
row_number() over (order by sal) row_number
from emp
order by sal
Данный запрос демонстрирует работу ранжирующих функций rank(), dense_rank() и row_number() по окладам работников. Обратите внимание на поведение данных функций в строках с одинаковыми значениями окладов.
select
ename,
deptno,
sal,
rank() over (partition by deptno order by sal) rank,
dense_rank() over (partition by deptno order by sal) dens_rank,
row_number() over (partition by deptno order by sal) row_number
from emp
order by deptno
Можно предыдущий запрос фрагментировать по отделам, то есть разбить на группы по отделам, в пределах которых будут работать аналитические функции, которые при выходе за пределы групп будут сбрасывать результаты. Результат представлен ниже
Рассмотрим пример применения функций lag() и lead(). Для того, чтобы можно было обращаться в текущей строке к предыдущим строкам, необходимо использовать функцию - lag(). Синтаксис ее таков
lag(поле_для_обращения, смещение, значение_для_замещения_null)
over (partition by выражение order by выражение)
поле_для_обращения - поле, по которому нужно просматривать значения;
смещение - смещенная строка, с которой просматривается поле, по-умолчанию равно 1, если проставить 0, тогда будет просматриваться текущее поле;
значение_для_замещения_null - по-умолчанию равно null, в случае отсутствия значения в просматриваемом поле, возвращает данное значение. Здесь стоит отметить, что подставляемое значение должно быть того же типа, что и просматриваемое поле;
для данной функции обязательно использование order by
with
main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from emp
order by sal
),
numerated_main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from main
)
select
ename,
sal,
lag(sal) over (order by rownum) previous_sal
from numerated_main
Или вот так
with
main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from emp
order by sal
),
numerated_main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from main
)
select
ename,
sal,
lag(sal, 2, 0) over (order by rownum) previous_sal
from numerated_main
Логика и синтаксис работы функции lead() аналогичен предыдущей функции с одной лишь разницей: просмотр идет не назад, а вперед
with
main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from emp
order by sal
),
numerated_main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from main
)
select
ename,
sal,
lead(sal, 2, 0) over (order by rownum) previous_sal
from numerated_main
Ниже для ознакомления приведен неполный список аналитических функций. Для более полной информации обращайтесь к документации. Всем успехов.
имя_функции(<аргумент>,< аргумент >, . . . )
over (<конструкция_фрагментации><конструкция_упорядочения><конструкция_окна>)
Рассмотрим основные части данного синтаксиса.
1. <конструкция_фрагментации>
Синтаксис для задания конструкции фрагментации выглядит следующим образом
partition by выражение [, выражение] [, выражение]
Данная конструкция логически разбивает результирующее множество на N групп
по критериям, задаваемым выражениями фрагментации. Аналитические функции применяются к каждой группе независимо, - для каждой новой группы они сбрасываются. Если не указать конструкцию фрагментации, все результирующее множество считается
одной группой.
2. <конструкция_упорядочения>
Конструкция упорядочения имеет следующий синтаксис
order by выражение [asc | desc] [nulls first | nulls last]
Конструкция order by задает критерий сортировки данных в каждой группе (в каждом фрагменте). Это, несомненно, влияет на результат выполнения любой аналитической функции. При наличии (или отсутствии) конструкции order by аналитические функции вычисляются по-другому. Например.
- без конструкции order by
select ename, sal, avg(sal) over ()
from emp
- с конструкцией order by
select ename, sal, avg(sal) over (order by ename)
from emp
Здесь стоит отметить следующее, на самом деле наличие конструкции order by в вызове аналитической функции добавляет стандартную конструкцию окна — RANGE UNBOUNDED PRECEDING. Это означает, что для вычисления используется набор из всех предыдущих и текущей строки в текущем фрагменте. При отсутствии order by стандартным окном является весь фрагмент. То есть по-сути предыдущий запрос будет выглядеть следующим образом
select ename, sal, avg(sal) over (order by ename RANGE UNBOUNDED PRECEDING)
from emp
3. <конструкция_окна>
Конструкция окна позволяет задать перемещающееся или жестко привязанное окно (набор)
данных в пределах группы, с которым будет работать аналитическая функция.
Например, для создания отчета, показывающего сумму зарплат текущего и двух предыдущих сотрудников отдела, можно выполнить следующий запрос
select deptno, ename, sal,
sum(sal) over (partition by deptno order by ename rows 2 preceding) sliding_total
from emp
order by deptno, ename
Можно создавать окна по двум критериям: по диапазону (RANGE) значений данных или по
смещению (ROWS) относительно текущей строки. Использование конструкции range как было сказано ранее в некоторых случаях используется неявно, RANGE UNBOUNDED PRECEDING например. Она требует брать все строки вплоть до текущей, в соответствии с порядком, задаваемым конструкцией order by. Следует помнить, что для использования окон
необходимо задавать конструкцию order by.
Окно определяется диапазоном строк, объединяемых в соответствии с заданным порядком.
Применять конструкцию range можно либо с числовыми выражениями (NUMBER), либо с выражениями, значением которого является дата (DATE). Еще одно ограничение для таких окон состоит в том, что в конструкции order by может быть только один столбец — диапазоны по природе своей одномерны. Нельзя задать диапазон в N-мерном пространстве. Пример.
Пусть необходимо выбрать зарплату каждого сотрудника и среднюю зарплату всех принятых на работу в течение 100 предыдущих дней, а также среднюю зарплату всех принятых на работу в течение 100 следующих дней. Соответствующий запрос будет выглядеть так:
select ename, hiredate, sal,
avg(sal) over (order by hiredate asc range 100 preceding) avg_sal_100_days_before,
avg(sal) over (order by hiredate desc range 100 preceding) avg_sal_100_days_after
from emp
order by hiredate desc
Помимо определения окна по диапазону (RANGE), также окна определяются и по количеству строк (ROWS). Для окон по строкам нет ограничений, присущих окнам по диапазону; данные могут быть любого типа и упорядочивать можно по любому количеству столбцов.
Например, пусть нужно вычислить среднюю зарплату для сотрудника и пяти принятых на работу до него и после него. Запрос можно записать следующим образом
select ename, hiredate, sal,
avg(sal) over (order by hiredate asc rows 5 preceding) avg_5_before,
avg(sal) over (order by hiredate desc rows 5 preceding) avg_5_after
from emp
order by hiredate
Зная как определяются окна (по диапазону или по количеству строк), рассмотрим как окончательно задаются окна. В простейшем случае, окно задается с помощью одной из трех следующих взаимоисключающих конструкций.
- UNBOUNDED PRECEDING.
Окно начинается с первой строки текущей группы и заканчивается текущей обрабатываемой строкой.
- CURRENT ROW.
Окно начинается (и заканчивается) текущей строкой.
- Числовое_выражение PRECEDING.
Окно начинается со строки за числовое_выражение строк до текущей, если оно задается по строкам, или со строки, меньшей по значению столбца, упомянутого в конструкции order by, не более чем на числовое выражение, если оно задается по диапазону.
- Числовое_выражение FOLLOWING.
Окно заканчивается (или начинается) со строки, через числовое_выражение строк после текущей, если оно задается по строкам, или со строки, большей
по значению столбца, упомянутого в конструкции order by, не более чем на числовое_выражение, если оно задается по диапазону.
Стоит отметить, что окно CURRENT ROW в простейшем виде, вероятно, никогда не используется, поскольку ограничивает применение аналитической функции одной текущей строкой, а для этого аналитические функции не нужны. В более сложном случае для окна задается также конструкция BETWEEN. В ней CURRENT ROW можно указывать в качестве начальной или конечной строки окна. Начальную и конечную строку окна в конструкции BETWEEN можно задавать с использованием любой из перечисленных выше конструкций. Например, можно задать окно так, что обрабатываемая строка не будет
последней, а окажется где-то в середине окна
select ename, hiredate,
first_value(ename) over (order by hiredate asc range between 100 preceding and 100 following),
last_value(ename) over (order by hiredate asc range between 100 preceding and 100 following)
from emp
order by hiredate asc
В данном запросе дополнительно использованы функции first_value() и last_value(), которые возвращают первое значений текущего окна и последнее значение также текущего окна, соответственно, в то время как диапазон окна ограничен слева текущая скользящая дата - 100 дней и справа к текущей скользящей дате + 100 дней, в этом и состоит смысл выражения - between 100 preceding and 100 following.
select
level,
count(*) over (order by level asc rows 2 preceding) asc_count,
count(*) over (order by level desc rows 2 preceding) desc_count
from dual
connect by level <= 10
order by level
Окно rows 2 preceding, как видно из результата запроса, содержит от 1 до 3 строк (это определяется тем, как далеко текущая строка находится от начала группы). Для первой строки группы имеем значение 1 (предыдущих строк нет). Для следующей строки в группе таких строк 2. Наконец, для третьей и далее строк значение count(*) остается постоянным, поскольку мы считаем только текущую строку и две предыдущие.
select
n,
sum(n) over (order by days range 2 preceding) n_sum,
days
from (
select
level n,
( to_date('10.01.2014', 'dd.mm.yyyy') + (level - 1) ) days
from dual
connect by level <= 10
)
order by days
В данном случае идет суммирование в пределах окна, диапазон которого составляет скользящий день и 2 предыдущих дня
select
ename,
sal,
rank() over (order by sal) rank,
dense_rank() over (order by sal) dens_rank,
row_number() over (order by sal) row_number
from emp
order by sal
Данный запрос демонстрирует работу ранжирующих функций rank(), dense_rank() и row_number() по окладам работников. Обратите внимание на поведение данных функций в строках с одинаковыми значениями окладов.
select
ename,
deptno,
sal,
rank() over (partition by deptno order by sal) rank,
dense_rank() over (partition by deptno order by sal) dens_rank,
row_number() over (partition by deptno order by sal) row_number
from emp
order by deptno
Можно предыдущий запрос фрагментировать по отделам, то есть разбить на группы по отделам, в пределах которых будут работать аналитические функции, которые при выходе за пределы групп будут сбрасывать результаты. Результат представлен ниже
Рассмотрим пример применения функций lag() и lead(). Для того, чтобы можно было обращаться в текущей строке к предыдущим строкам, необходимо использовать функцию - lag(). Синтаксис ее таков
lag(поле_для_обращения, смещение, значение_для_замещения_null)
over (partition by выражение order by выражение)
поле_для_обращения - поле, по которому нужно просматривать значения;
смещение - смещенная строка, с которой просматривается поле, по-умолчанию равно 1, если проставить 0, тогда будет просматриваться текущее поле;
значение_для_замещения_null - по-умолчанию равно null, в случае отсутствия значения в просматриваемом поле, возвращает данное значение. Здесь стоит отметить, что подставляемое значение должно быть того же типа, что и просматриваемое поле;
для данной функции обязательно использование order by
with
main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from emp
order by sal
),
numerated_main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from main
)
select
ename,
sal,
lag(sal) over (order by rownum) previous_sal
from numerated_main
Или вот так
with
main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from emp
order by sal
),
numerated_main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from main
)
select
ename,
sal,
lag(sal, 2, 0) over (order by rownum) previous_sal
from numerated_main
Логика и синтаксис работы функции lead() аналогичен предыдущей функции с одной лишь разницей: просмотр идет не назад, а вперед
with
main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from emp
order by sal
),
numerated_main as (
select empno, ename, job, mgr, hiredate, sal, comm, deptno, rownum
from main
)
select
ename,
sal,
lead(sal, 2, 0) over (order by rownum) previous_sal
from numerated_main
Ниже для ознакомления приведен неполный список аналитических функций. Для более полной информации обращайтесь к документации. Всем успехов.
Аналитическая функция | Описание |
AVG([DISTINCT | ALL] выражение) | Используется для вычисления среднего значения выражения в пределах группы и окна. Для поиска среднего после удаления дублирующихся значений можно указывать ключевое слово DISTINCT |
CORR(выражение, выражение) | Выдает коэффициент корреляции для пары выражений, возвращающих числовые значения. В статистическом смысле, корреляция — это степень связи между переменными. Связь между переменными означает, что значение одной переменной можно в определенной степени предсказать по значению другой. Коэффициент корреляции представляет степень корреляции в виде числа в диапазоне от -1 (высокая обратная корреляция) до 1 (высокая корреляция). Значение 0 соответствует отсутствию корреляции |
COUNT([DISTINCT][*] [выражение]) | Эта функция считает строки в группах. Если указать * или любую константу, кроме NULL, функция COUNT будет считать все строки. Если указать выражение, функция COUNT будет считать строки, для которых выражение имеет значение не NULL. Можно задавать модификатор DISTINCT, чтобы считать строки в группах после удаления дублирующихся строк |
COVAR_POP(выражение, выражение) | Возвращает ковариацию генеральной совокупности (population covariance) пары выражений с числовыми значениями |
COVAR_SAMP(выражение, выражение) | Возвращает выборочную ковариацию (sample covariance) пары выражений с числовыми значениями |
CUME_DIST | Вычисляет относительную позицию строки в группе. Функция CUME_DIST всегда возвращает число большее 0 и меньше или равное 1. Это число представляет "позицию" строки в группе из N арок. В группе из трех строк, например, возвращаются следующие значения кумулятивного распределения: 1/3, 2/3 и 3/3 |
DENSE_RANK | Эта функция вычисляет относительный ранг каждой возвращаемой запросом строки по отношению к другим строкам, основываясь на значениях выражений в конструкции ORDER BY. Данные в группе сортируются в соответствии с конструкцией ORDER BY, а затем каждой строке поочередно присваивается числовой ранг, начиная с 1. Ранг увеличивается при каждом изменении значений выражений, входящих в конструкцию ORDER BY. Строки с одинаковыми значениями получают один и тот же ранг (при этом сравнении значения NULL считаются одинаковыми). Возвращаемый этой функцией "плотный" ранг дает ранговые значения без промежутков. Сравните с представленной далее функцией RANK |
FIRST_VALUE | Возвращает первое значение в группе |
LAG(выражение,<смещение>, <стандартное значение>) | Функция LAG дает доступ к другим строкам результирующего множества, избавляя от необходимости выполнять самосоединения. Она позволяет работать с курсором как с массивом. Можно ссылаться на строки, предшествующие текущей строке в группе. О том, как обращаться к следующим строкам в группе, см. в описании функции LEAD. Смещение - это положительное целое число со стандартным значением 1 (предыдущая строка). Стандартное значение возвращается, если индекс выходит за пределы окна (для первой строки группы будет возвращено стандартное значение) |
LAST_VALUE | Возвращает последнее значение в группе |
LEAD(выpaжeниe,<смещение>,<стандартное значение>) | Функция LEAD противоположна функции LAG. Если функция LAG дает доступ к предшествующим строкам группы, то функция LEAD позволяет обращаться к строкам, следующим за текущей. Смещение — это положительное целое число со стандартным значением 1 (следующая строка). Стандартное значение возвращается, если индекс выходит за пределы окна (для последней строки группы будет возвращено стандартное значение) |
МАХ(выражение) | Находит максимальное значение выражения в пределах окна в группе |
МIN(выражение) | Находит минимальное значение выражения в пределах окна в группе |
NTILE(выражение) | Делит группу на фрагменты по значению выражения. Например, если выражение = 4, то каждой строке в группе присваивается число от 1 до 4 в соответствии с фрагментом, в которую она попадает. Если в группе 20 строк, первые 5 получат значение 1, следующие 5 — значение 2 и т.д. Если количество строк в группе не делится на значение выражения без остатка, строки распределяются так, что ни в одном фрагменте количество строк не превосходит минимальное количество в других фрагментах более чем на 1, причем дополнительные строки будут в группах с меньшими номера фрагмента. Например, если снова выражение = 4, а количество строк = 21, в первом фрагменте будет 6 строк, во втором и последующих - 5 |
PERCENT RANK | Аналогична функции CUME_DIST (кумулятивное распределение). Вычисляет ранг строки в группе минус 1, деленный на количество обрабатываемых строк минус 1. Эта функция всегда возвращает значения в диапазоне от 0 до 1 включительно |
RANK | Эта функция вычисляет относительный ранг каждой строки, возвращаемой запросом, на основе значений выражений, входящих в конструкцию ORDER BY. Данные в группе сортируются в соответствии с конструкцией ORDER BY, а затем каждой строке поочередно присваивается числовой ранг, начиная с 1. Строки с одинаковыми значениями выражений, входящих в конструкцию ORDER BY, получают одинаковый ранг, но если две строки получат одинаковый ранг, следующее значение ранга пропускается. Если две строки получили ранг 1, строки с рангом 2 не будет; следующая строка в группе получит ранг 3. В этом отличие от функции DENSE_RANK, которая не пропускает значений |
RATIO_TO_REPORT(выражение) | Эта функция вычисляет значение выражение / (SUM(выражение)) по строкам группы. Это дает процент, который составляет значение текущей строки по отношению к SUM(выражение) |
REGR_xxxxxxx(выражение,выражение) | Эти функции линейной регрессии применяют стандартную линейную регрессию по методу наименьших квадратов к паре выражений. Предлагается 9 различных функций регрессии |
ROW_NUMBER | Возвращает смещение строки по отношению к началу упорядоченной группы. Может использоваться для последовательной нумерации строк, упорядоченных по определенным критериям |
STDDEV(выражение) | Вычисляет стандартное (среднеквадратичное) отклонение (standard deviation) текущей строки по отношению к группе |
STDDEV_POP(выражение) | Эта функция вычисляет стандартное отклонение генеральной совокупности (population standard deviation) и возвращает квадратный корень из дисперсии генеральной совокупности (population variance). Она возвращает значение, совпадающее с квадратным корнем из результата функции VAR_POP |
STDDEV_SAMP(выражение) | Эта функция вычисляет накопленное стандартное отклонение выборки (cumulative sample standard deviation) и возвращает квадратный корень выборочной дисперсии (sample variance). Она возвращает значение, совпадающее с квадратным корнем из результата функции VAR_SAMP |
SUM(выражение) | Вычисляет общую сумму значений выражения для группы |
VAR_POP(выражение) | Эта функция возвращает дисперсию генеральной совокупности для набора числовых значений (значения NULL игнорируются) |
VAR_SAMP(выражение) | Эта функция возвращает выборочную дисперсию длянабора числовых значений (значения NULL игнорируются) |
VARIANCE(выражение) | Возвращает дисперсию для выражения. Сервер Oracle вычисляет дисперсию как: - 0, если количество строк в группе = 1; -VAR_SAMP, если количество строк в группе больше 1 |
Ubuntu Server/Apache2/PHP. Настройка для работы с htaccess
Начнем с установки. Перед установкой веб сервера обновим систему. Обновим список репозиториев и произведем поиск индексов обновленных версий программ, драйверов, ядра и всего прочего
sudo apt-get update
Далее установим все доступные обновления
sudo apt-get upgrade
После этого приступим к установке apache2 и php5
sudo apt-get install apache2
sudo apt-get install php5
Для проверки успешности установки веб-сервера введите в браузере - http://127.0.0.1
Сервер должен вернуть сообщение похожее на - "It`s work". Далее нужно включить поддержку
php5 в apache. Для этого установим необходимый пакет с последующим включением модуля:
sudo apt-get install libapache2-mod-php5
sudo a2enmod php5
Без этого пакета при обращении к вашему сайту (предполагается, что в корне лежит индексный php-файл), веб-сервер будет предлагать вам сохранить копию корневого php-файла.
Теперь займемся созданием нашего тестового сайта - test.ru
Во первых - определим корневую директорию нашего сайта. По умолчанию после установки apache2 в ubuntu server будет создан default сайт, с корнем в /var/www
В его недрах будет лежать тот самый html-файл, который возвращает сообщение о работоспособности нашего веб-сервера. Давайте займем его место. Для этого нужно перейти в раздел - /etc/apache2/sites-available:
cd /etc/apache2/sites-av*
и далее создать файл конфигурации для нашего сайта (я использую редактор nano)
sudo nano test
В нем пропишем на первых порах следующие настройки:
<VirtualHost *:80>
DocumentRoot /var/www/
ServerName test.ru
ServerAdmin test@gmail.com
<Directory /var/www/>
DirectoryIndex index.php
AllowOverride All
</Directory>
<FilesMatch "^\.ht">
Order allow,deny
Allow from all
Satisfy All
</FilesMatch>
</VirtualHost>
Сохраняем внесенные настройки. Далее деактивируем первоначальный сайт и активируем наш тестовый сайт
sudo a2dissite default && sudo a2ensite test.ru
Для предупреждения такого рода ошибки
apache2: Could not determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
нужно либо в свой файл настроек, либо в /etc/apache2/httpd.conf, либо в /etc/apache2/conf.d, создать свой файл настроек, и вписать туда
ServerName localhost
Я выбрал второй вариант. Также сюда можно поместить и такие настройки
## Для того, чтобы Apache интерпретировал php и не предлагал сохранить php-файл
AddType application/x-httpd-php .php .phtml
## Установка кодировки UTF-8 по умолчанию
AddDefaultCharset UTF-8
Но это можно будет сделать и в .htaccess. Далее внесем имя сайта в список хостов сервера
sudo nano /etc/hosts
и впишем через пробел имя нашего сайта
127.0.0.1 localhost test.ru
Далее очищаем директорию /var/www и создаем корневой php-файл - index.php - вот с таким содержимым:
<?php
phpinfo();
?>
Перезагружаем apache
sudo /etc/init.d/apache2 restart
Пробуем обратиться по адресу http://test.ru в веб браузере и если все сделано правильно, на выходе должен появиться массив сообщений о текущих настройках php. Кстати файл настроек php лежит в директории (помним, что в нашем случае php собран как модуль apache)
/etc/php5/apache2/php.ini
Далее создаем файл .htaccess в той же директории, где расположен index.php и добавляем в него следующие правила (так как в файле конфигурации в Directory указано - AllowOverride All, значит .htaccess будет переопределять все директивы apache)
AddDefaultCharset utf8
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path1=$1&var=2¶m=%{QUERY_STRING} [L]
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path1=$1&var=1¶m=%{QUERY_STRING} [L]
Для проверки работы данных правил добавим в index.php следующие строчки
<?php
echo 'test.ru';
echo '</br>';
print_r($_REQUEST);
?>
Теперь пробуем набрать в адресной строке браузера http://test.ru/tester/?p=16
Если все хорошо, значит на выходе должны получить следующее сообщение:
test.ru
Array ( [path] => tester [var] => 2 [param] => p=16 )
Если же будете получать 500 ошибку (internal server error), то либо вы допустили синтаксическую ошибку, либо у вас не задействован модуль mod_rewrite. Для диагностики можно попробовать поправить правила в htaccess следующим образом
AddDefaultCharset utf8
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path1=$1&var=2¶m=%{QUERY_STRING} [L]
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path1=$1&var=1¶m=%{QUERY_STRING} [L]
</IfModule>
Если данная ошибка пропадет, но правила все также не будут работать, значит у вас точно не включен нужный модуль. Включаем данный модуль
sudo a2enmod rewrite
И снова проверяем работу правил htaccess. Все должно непременно заработать.
Если после настройки веб-сервера, что-то все-таки будет работать некорректно, стоит переустановить apache. Для его удаления введите команду
sudo apt-get purge apache2 apache2-utils apache2.2-bin apache2-common
И не забудьте очистить/удалить следующие директории
/etc/apache2
/var/www
Также стоит отметить, если на сервере не поднят DNS-сервер, тогда обращение к нашему тестовому сайту будет напрямую через его ip-адрес - http://XX.XX.XX.XX, чтобы все-таки стучаться к нему по его имени, стоит либо поднять DNS-службу, либо прописать на конечном компе - откуда вы будете стучаться - в хостах соответствие ip сервера его имени
XX.XX.XX.XX test.ru
Для Win32/64 как правило данное соответствие расположено в директории
C:\Windows\System32\drivers\etc\hosts
Всем удачи :)
sudo apt-get update
Далее установим все доступные обновления
sudo apt-get upgrade
После этого приступим к установке apache2 и php5
sudo apt-get install apache2
sudo apt-get install php5
Для проверки успешности установки веб-сервера введите в браузере - http://127.0.0.1
Сервер должен вернуть сообщение похожее на - "It`s work". Далее нужно включить поддержку
php5 в apache. Для этого установим необходимый пакет с последующим включением модуля:
sudo apt-get install libapache2-mod-php5
sudo a2enmod php5
Без этого пакета при обращении к вашему сайту (предполагается, что в корне лежит индексный php-файл), веб-сервер будет предлагать вам сохранить копию корневого php-файла.
Теперь займемся созданием нашего тестового сайта - test.ru
Во первых - определим корневую директорию нашего сайта. По умолчанию после установки apache2 в ubuntu server будет создан default сайт, с корнем в /var/www
В его недрах будет лежать тот самый html-файл, который возвращает сообщение о работоспособности нашего веб-сервера. Давайте займем его место. Для этого нужно перейти в раздел - /etc/apache2/sites-available:
cd /etc/apache2/sites-av*
и далее создать файл конфигурации для нашего сайта (я использую редактор nano)
sudo nano test
В нем пропишем на первых порах следующие настройки:
<VirtualHost *:80>
DocumentRoot /var/www/
ServerName test.ru
ServerAdmin test@gmail.com
<Directory /var/www/>
DirectoryIndex index.php
AllowOverride All
</Directory>
<FilesMatch "^\.ht">
Order allow,deny
Allow from all
Satisfy All
</FilesMatch>
</VirtualHost>
Сохраняем внесенные настройки. Далее деактивируем первоначальный сайт и активируем наш тестовый сайт
sudo a2dissite default && sudo a2ensite test.ru
Для предупреждения такого рода ошибки
apache2: Could not determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
нужно либо в свой файл настроек, либо в /etc/apache2/httpd.conf, либо в /etc/apache2/conf.d, создать свой файл настроек, и вписать туда
ServerName localhost
Я выбрал второй вариант. Также сюда можно поместить и такие настройки
## Для того, чтобы Apache интерпретировал php и не предлагал сохранить php-файл
AddType application/x-httpd-php .php .phtml
## Установка кодировки UTF-8 по умолчанию
AddDefaultCharset UTF-8
Но это можно будет сделать и в .htaccess. Далее внесем имя сайта в список хостов сервера
sudo nano /etc/hosts
и впишем через пробел имя нашего сайта
127.0.0.1 localhost test.ru
Далее очищаем директорию /var/www и создаем корневой php-файл - index.php - вот с таким содержимым:
<?php
phpinfo();
?>
Перезагружаем apache
sudo /etc/init.d/apache2 restart
Пробуем обратиться по адресу http://test.ru в веб браузере и если все сделано правильно, на выходе должен появиться массив сообщений о текущих настройках php. Кстати файл настроек php лежит в директории (помним, что в нашем случае php собран как модуль apache)
/etc/php5/apache2/php.ini
Далее создаем файл .htaccess в той же директории, где расположен index.php и добавляем в него следующие правила (так как в файле конфигурации в Directory указано - AllowOverride All, значит .htaccess будет переопределять все директивы apache)
AddDefaultCharset utf8
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path1=$1&var=2¶m=%{QUERY_STRING} [L]
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path1=$1&var=1¶m=%{QUERY_STRING} [L]
Для проверки работы данных правил добавим в index.php следующие строчки
<?php
echo 'test.ru';
echo '</br>';
print_r($_REQUEST);
?>
Теперь пробуем набрать в адресной строке браузера http://test.ru/tester/?p=16
Если все хорошо, значит на выходе должны получить следующее сообщение:
test.ru
Array ( [path] => tester [var] => 2 [param] => p=16 )
Если же будете получать 500 ошибку (internal server error), то либо вы допустили синтаксическую ошибку, либо у вас не задействован модуль mod_rewrite. Для диагностики можно попробовать поправить правила в htaccess следующим образом
AddDefaultCharset utf8
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path1=$1&var=2¶m=%{QUERY_STRING} [L]
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path1=$1&var=1¶m=%{QUERY_STRING} [L]
</IfModule>
Если данная ошибка пропадет, но правила все также не будут работать, значит у вас точно не включен нужный модуль. Включаем данный модуль
sudo a2enmod rewrite
И снова проверяем работу правил htaccess. Все должно непременно заработать.
Если после настройки веб-сервера, что-то все-таки будет работать некорректно, стоит переустановить apache. Для его удаления введите команду
sudo apt-get purge apache2 apache2-utils apache2.2-bin apache2-common
И не забудьте очистить/удалить следующие директории
/etc/apache2
/var/www
Также стоит отметить, если на сервере не поднят DNS-сервер, тогда обращение к нашему тестовому сайту будет напрямую через его ip-адрес - http://XX.XX.XX.XX, чтобы все-таки стучаться к нему по его имени, стоит либо поднять DNS-службу, либо прописать на конечном компе - откуда вы будете стучаться - в хостах соответствие ip сервера его имени
XX.XX.XX.XX test.ru
Для Win32/64 как правило данное соответствие расположено в директории
C:\Windows\System32\drivers\etc\hosts
Всем удачи :)
Apache2/htaccess. Фильтрация ссылок
Небольшое лирическое отступление. Как известно, htaccess способен переопределять директивы apache в своей директории, а также в поддиректориях (если там нет своих htaccess). Это при условии, что в самом apache подключен соответствующий модуль mod_rewrite и разрешено переопределение директив. Также htaccess работает со статическими ссылками, преобразуя их в динамические по прописанным в нем правилам. В общем сие сказанное можно представить следующим рисунком.
Желтым цветом помечено само преобразование, а красным для наглядности выделен некий входной параметр, все зависит от логики вашего веб приложения.
Разработаем правила, по которым мы сможем определенным образом фильтровать ссылки:
- до первого разделителя будем отбирать относительные пути к разделам нашего тестового сайта (path);
- все остальное будем игнорировать, но не забудем и про параметры GET запросов (param).
Для случая http://test.ru/abc
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
Для случая http://test.ru/abc/ или http://test.ru/abc/def
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
В итоге получаем:
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
Желтым цветом помечено само преобразование, а красным для наглядности выделен некий входной параметр, все зависит от логики вашего веб приложения.
Разработаем правила, по которым мы сможем определенным образом фильтровать ссылки:
- до первого разделителя будем отбирать относительные пути к разделам нашего тестового сайта (path);
- все остальное будем игнорировать, но не забудем и про параметры GET запросов (param).
Для случая http://test.ru/abc
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
Для случая http://test.ru/abc/ или http://test.ru/abc/def
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
В итоге получаем:
RewriteEngine On
RewriteRule ^([a-zA-Z0-9]+)/(.*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
RewriteRule ^([a-zA-Z0-9]*)$ index.php?path=$1¶m=%{QUERY_STRING} [L]
Subscribe to:
Posts
(
Atom
)