Как известно, со стороны 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, тогда будет получен правильный ответ
Всем хорошего дня :)
Subscribe to:
Posts
(
Atom
)




