2016-05-12 5 views
0

У меня есть следующие таблицы MySql.Неожиданное поведение оператора UNION ALL

Таблица tblUsg определяется как таковую:

CREATE TABLE `tblUsg` (
`id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
`ip` VARCHAR(46) NOT NULL, 
`dtm` DATETIME NOT NULL, 
`huid` BINARY(32) NOT NULL, 
`licnm` VARCHAR(20) NOT NULL, 
`lichld` VARCHAR(256) NOT NULL, 
`flgs` INT NOT NULL, 
`agnt` VARCHAR(256), 

INDEX `ix_huid` (`huid`), 
INDEX `ix_licnm` (`licnm`), 
UNIQUE KEY `ix_lichuid` (`huid`, `licnm`) 
) AUTO_INCREMENT=0 CHARACTER SET utf8 COLLATE utf8_unicode_ci; 

И таблица tblLics определяется как таковую:

CREATE TABLE `wosLics` (
`id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY, 
`licnm` VARCHAR(20) NOT NULL, 
`desc` VARCHAR(256) NOT NULL, 
`maxcpy` INT NOT NULL, 
`dtmFrom` DATETIME, 
`dtmTo` DATETIME, 
`stat` INT NOT NULL, 

UNIQUE KEY `ix_licnm` (`licnm`) 
) AUTO_INCREMENT=0 CHARACTER SET utf8 COLLATE utf8_unicode_ci; 

Я затем вызвать следующий скрипт, когда, скажем, обе таблицы пусты:

$link = @mysql_connect($HOSTNAME, $USERNAME, $PASSWD); 
@mysql_select_db($DBNAME); 
mysql_set_charset('utf8', $link); 

$res = @mysql_query(
    "SELECT `maxcpy`, `stat`, `dtmFrom`, `dtmTo` FROM `tblLics` WHERE `licnm`='zbcdefghijklmnopqrsu'\n". 
    "UNION ALL\n". 
    "SELECT COUNT(*), NULL, NULL, NULL FROM `tblUsg` WHERE `licnm`='zbcdefghijklmnopqrsu'\n". 
    "UNION ALL\n". 
    "SELECT COUNT(*), NULL, NULL, NULL FROM `tblUsg` WHERE (`licnm`='zbcdefghijklmnopqrsu' AND `huid`='a871c47a7f48a12b38a994e48a9659fab5d6376f3dbce37559bcb617efe8662d')" 
    , $link); 
if($res) 
{ 
    $row0 = @mysql_fetch_row($res); 
    $row1 = @mysql_fetch_row($res); 
    $row2 = @mysql_fetch_row($res); 

    echo("<br/>0::<br/>"); 
    var_dump($row0); 
    echo("<br/>1::<br/>"); 
    var_dump($row1); 
    echo("<br/>2::<br/>"); 
    var_dump($row2); 
} 

Какие выходы:

0:: 
array(4) { [0]=> string(1) "0" [1]=> NULL [2]=> NULL [3]=> NULL } 
1:: 
array(4) { [0]=> string(1) "0" [1]=> NULL [2]=> NULL [3]=> NULL } 
2:: 
bool(false) 

Мой вопрос, почему мой $row2 является false когда $row1 является массив, как я бы ожидал?

+1

Не правда ли? У вас есть запись, которая удовлетворяет условию where в первом запросе? Каков ваш ожидаемый результат для пустого результата и два набора записей с 1 записью в каждом? – Pred

+0

@Pred: Нет, таблицы изначально пустые. В этом случае я ожидал бы, что моя '$ row0' будет' false'. Разве это не так? – c00000fd

+0

Почему? Пусто + Что-то = Что-то. SQL не будет переиндексировать результат только потому, что у вас есть пустой набор где-то. Как SQL должен знать, должен ли первый запрос возвращать 1 или 2 или ни один или 10k записей? Где он должен переиндексировать записи (и заполнять все до первой записи)? – Pred

ответ

1

Мой вопрос, почему мой $row2 ложна, когда $row1 является массив, как я бы ожидал?

Ожидается, что вы вернете 3 строки из своего запроса, но он возвращает только 2 строки.

Ваш запрос UNION s три SELECT s. Каждый из двух последних SELECT s всегда возвращает ровно одну строку. Первый SELECT может возвращать 0 строк или более. Поскольку таблица пуста, она возвращает ровно нулевые строки.

0+1+1. Запрос возвращает ровно 2 строк.


Update:

Вы ожидаете, что строки должны быть возвращены в определенном порядке, но запрос не требует какой-либо сортировки. SQL работает с наборами строк, а наборы, как математические объекты, представляют собой несортированные коллекции (и именно так SQL обрабатывает их).

Без запроса ORDER BY в запросе строки, возвращаемые UNION, не гарантируются в любом порядке. Даже порядок, который они приходят от SELECT, не сохраняется.

Если вы хотите, чтобы получить строки в порядке вы написали SELECT запросы, то вы должны добавить дополнительный столбец, который говорит порядок и использовать в в ORDER BY пункте:

SELECT `maxcpy`, `stat`, `dtmFrom`, `dtmTo`, 1 AS tableNb 
FROM `tblLics` 
WHERE `licnm`='zbcdefghijklmnopqrsu' 

UNION ALL 

SELECT COUNT(*), NULL, NULL, NULL, 2 AS tableNb 
FROM `tblUsg` 
WHERE `licnm`='zbcdefghijklmnopqrsu' 

UNION ALL 

SELECT COUNT(*), NULL, NULL, NULL, 3 AS tableNb 
FROM `tblUsg` 
WHERE `licnm`='zbcdefghijklmnopqrsu' 
    AND `huid`='a871c47a7f48a12b38a994e48a9659fab5d6376f3dbce37559bcb617efe8662d' 

ORDER BY tableNb 

Таким образом, вы знаете, какая часть запроса сгенерировала каждую из возвращаемых строк.

Примечание

Вам не нужно строку, возвращаемую вторым запросом. В основном это говорит вам, сколько строк возвращается первым запросом, но вы также можете знать, что подсчитывая строки, имеющие tableNb == 1 в результирующем наборе.Так как вы хотите подсчет после фактических строк, ему не нужен дополнительный обход результирующего набора, это можно сделать, указав строки из первого запроса.

+0

Спасибо. Наверное, я неправильно понял значение «ВСЕ» в «UNION ALL». Мое предположение заключалось в том, что оно будет включать также NULL. Плохо. Так что мое строительство выше в значительной степени бесполезно. Поэтому мне придется разделить его на 3 отдельных вызова 'mysql_query'. Виртуализация параллелизма. – c00000fd

+0

'ALL' часть' UNION ALL' не удаляет повторяющиеся строки. Но первый SELECT не возвращает ни одной строки. Если бы он возвращал строку, полную «NULL', эта строка была бы возвращена в окончательный набор результатов. – axiac