Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2015.09.10;
Скачать: [xml.tar.bz2];

Вниз

глюк MS SQL + ADO в асинхронном режиме   Найти похожие ветки 

 
Zikurat ©   (2014-12-21 14:50) [0]

Долго разбирался и наконец докопался до крайне странного глюка, когда ADO после запроса к MS SQL оказывается в очень непонятном состоянии.

Проблема точно воспроизводится на MS SQL 2005 + Delphi XE, другое не тестировал, но было бы любопытно, если камрады сообщат.

Итак, исполнение запроса и фетчинг ставим в асинхронный режим:

ADOQuery1.ExecuteOptions := ADOQuery1.ExecuteOptions + [eoAsyncExecute] + [eoAsyncFetch];

Запрос в ADOQuery1.SQL.Text должен быть таким:

SET NOCOUNT ON;

declare @max_timestamp timestamp;

select @max_timestamp=max(timestamp) from
 (
     select max(timestamp) timestamp from Table1
     union
     select max(timestamp) timestamp from Table2
 ) t;

select @max_timestamp;


Суть запроса - выбираем максимальный Timestamp из двух таблиц. Вроде все просто.

Для повторения глюка в Table1 нужно иметь записи, а вот Table2 должна быть пустой! Это обязательное условие, также обязателен union, обязательно вводить переменную @max_timestamp, иначе опять же глюка не получится (если например просто select сделать, а не в переменную).

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


 
Zikurat ©   (2014-12-21 14:55) [1]

Запрос можно переделать на, допустим, такой:

SET NOCOUNT ON;

select 5 MyColumn;  --  <-- ДОБАВИЛИ

declare @max_timestamp timestamp;

select @max_timestamp=max(timestamp) from
 (
     select max(timestamp) timestamp from Table1
     union
     select max(timestamp) timestamp from Table2
 ) t;

select @max_timestamp;


Тогда запрос проходит, событие фетчинга - всё ок, понятно что мы получили набор данным в виде колонки MyColumn и значения 5.
При попытке перейти на следующий рекордсет - пишется ошибка, что рекордсет не открыт!

var
 i: integer;
...
 ADOQuery1.Recordset := ADOQuery1.NextRecordset(i); // исключение


Если же сделать наоборот:

SET NOCOUNT ON;

declare @max_timestamp timestamp;

select @max_timestamp=max(timestamp) from
 (
     select max(timestamp) timestamp from Table1
     union
     select max(timestamp) timestamp from Table2
 ) t;

select @max_timestamp;

select 5 MyColumn;  --  <-- ДОБАВИЛИ


То есть, первым подставить наш глючный рекордсет, то тогда при выполнении:

var
 i: integer;
...
 ADOQuery1.Recordset := ADOQuery1.NextRecordset(i);


то ADO намертво навсегда зависнет.


 
Zikurat ©   (2014-12-21 14:57) [2]

Стоит добавить в Table2 хоть одну запись - все начинает работать супер.

Стоит написать select iNull(max(timestamp), 0) timestamp from Table2 - все опять же начинает работать.

В таком варианте (без ввода переменной):

SET NOCOUNT ON;

declare @max_timestamp timestamp;

select max(timestamp) timestamp from
 (
     select max(timestamp) timestamp from Table1
     union
     select max(timestamp) timestamp from Table2
 ) t;


Тоже отлично работает.

Интересно - что за бага?!


 
Ega23 ©   (2014-12-21 16:52) [3]

select @max_timestamp=max(timestamp) from
 (
     select max(timestamp) timestamp from Table1
     union
     select max(timestamp) timestamp from Table2
 ) t;

А если так


select @max_timestamp=isnull(max(t.ts), 0) from
 (
     select isnull(max(timestamp), 0) ts from Table1
     union
     select isnull(max(timestamp), 0) ts from Table2
 ) t;


 
Zikurat ©   (2014-12-21 17:08) [4]


> А если так

а если так - то смотри пост [2]


 
Ega23 ©   (2014-12-21 18:40) [5]

Тогда в чём проблема? null union something даёт null


 
Zikurat ©   (2014-12-21 19:44) [6]


> null union something даёт null

Олег, ты жжошь ;))

Во-первых, null union something дает не null, а массив записей, где есть null и something

Не надо путать логические условия и объединение результатов запроса.

Во-вторых, max от (null union something) результатом дает something, если он не null.

В-третьих, как это всё может объяснить зависание и глюки ADO? Если БД + провайдер на некоторый запрос возвращает null - это абсолютно нормально, если же возвращается нечто, к чему нельзя обратиться, а в некоторых ситуациях происходит глобальное зависание + потери памяти при освобождении объектов ADO - причем здесь null?!


 
Zikurat ©   (2014-12-21 19:51) [7]

К тому же, ежу понятно, что в случае наличия записей в Table1 и отсутствия записей в Table2 - результат выборки будет зависеть исключительно от записей Table1. Добавление записей в Table2 с timestamp ниже, чем у записей в Table1 по идее ничего не должен изменить с точки зрения результата. Все равно будет выбрана максимальная запись из Table1.

Но по факту добавление любой записи в Table2 приводит к пропаданию глюка, при том, что результат запроса не изменяется.


 
sniknik ©   (2014-12-22 00:56) [8]

> Все равно будет выбрана максимальная запись из Table1.
она будет выбрана в переменную, рекордсет же первый будет пустой
-
Выполнено применительно к -1 записям. (no recordset)
            применительно к 1 записям

для
declare @max_timestamp timestamp;

select @max_timestamp=max(ts) from
 (
     select max(ts) ts from Table1
     union
     select max(ts) ts from Table2
 ) t;

select @max_timestamp;


нокаунт не нужен, тут нет инсертов (хотя и с ним пробовал), а timestamp для имен полей ... зарезервированное слово (может из-за этого глюк? хотя и так тоже пробовал).
нет глюка.

попробуй заменить ADOQuery на ADODataSet.


 
sniknik ©   (2014-12-22 01:21) [9]

> попробуй заменить ADOQuery на ADODataSet.
не получится... понял в чем проблема.

это описанный случай, первый пустой рекордсет от переменной
> Выполнено применительно к -1 записям. (no recordset)
>            применительно к 1 записям

а вот что получается если обе таблицы имеют данные
> Выполнено применительно к 1 записям
первого как бы не существует, причем не зависимо включен SET NOCOUNT ON; или выключен SET NOCOUNT OFF;

я в тесте на Command-ах все делаю поэтому и не увидел сначала.


 
Zikurat ©   (2014-12-23 18:18) [10]

sniknik, не совсем полностью я тебя понял.
У меня по сути вопроса два:

1) что это за глюк, MS SQL / ADO?
Интересно было бы даже потеоретизировать...

2) на примере TADOQuery интересно как то можно понять, что мы получили "глючный" датасет? И если он глючный - как то можно переключиться на следующий? Потому что при попытке переключения на следующий - идет зависание, если использовать NextRecordset (см. [1] в конце).


 
sniknik ©   (2014-12-26 02:05) [11]

> 1) что это за глюк, MS SQL / ADO?
ИМХО MS SQL, возвращает на "не возвращающей" команде. все равно, что дельфя при присвоении переменной (i:= 1;) вдруг начала бы форму рисовать. в ADO уже последствия.

> И если он глючный - как то можно переключиться на следующий?
на датасете не получится, он для данных, их обработки, что и приводит к глюку при отсутствующих/кривых данных. а вот на ADOCommand-ах, рекордсетах, легко (вон я даже не заметил поначалу). + там и ошибку возвращенную прочитать можно.


 
sniknik ©   (2014-12-26 08:01) [12]

> + там и ошибку возвращенную прочитать можно.
вот, что получается при чтении ошибки каждого рекордсета -

8153 : Внимание! Значение NULL исключено в статистических или других операциях SET.
RecAff - 1


 
sniknik ©   (2014-12-26 08:18) [13]

http://msdn.microsoft.com/ru-ru/library/ms190368%28SQL.90%29.aspx

мелкософт не считает это ошибкой, предупреждением, а то что из-за этого (возврата этого предупреждения) ломается логика обработки данных... ну это проблема обработчика.

т.е. глюк c их точки зрения не их а delphi. хотя причиной стало внутреннее предупреждение.


 
Zikurat ©   (2014-12-26 18:29) [14]


> 8153 : Внимание! Значение NULL исключено в статистических
> или других операциях SET.
> RecAff - 1

где ты всё это читаешь?!

Я вот переделал на ADOCommand, свой запрос также ввожу в CommandText, исполняю Execute, точно также выполнение запроса определяется по событию у TAdoConnect.OnExecuteComplete, там статус, что запрос прекрасно исполнился (EventStatus = esOk). Если попытаться присвоить Recordset, то всплывает, что рекордсет не открыт.

procedure TForm1.ADOConnection1ExecuteComplete(Connection: TADOConnection;
 RecordsAffected: Integer; const Error: Error;
 var EventStatus: TEventStatus; const Command: _Command;
 const Recordset: _Recordset);
begin
 if EventStatus = esOk then
   MyAdoDataset.Recordset := Recordset;
...


А откуда ты свои ошибки берешь?!


 
sniknik ©   (2014-12-26 22:03) [15]

> А откуда ты свои ошибки берешь?!
у меня нет ошибок... :)

сообщение об ошибке MSSQL из ADO, как говорил -
> а вот на ADOCommand-ах, рекордсетах, легко
у тебя же  MyAdoDataset.Recordset := Recordset; а он -
> для данных, их обработки, что и приводит к глюку при отсутствующих/кривых данных.

вот так для твоего глючного случая получится
var
 V: OleVariant;  
begin
 if EventStatus = esOk then
   MyAdoDataset.Recordset := Recordset.NextRecordset(V);
...


 
sniknik ©   (2014-12-26 22:16) [16]

> где ты всё это читаешь?!
вариантно, смотря как/для чего писать
коллекция
ADOConnection1.Errors
событие
ADOConnection1InfoMessage
обьект
Conn: _Connection;
Conn:= CreateComObject(CLASS_Connection) as _Connection;
Conn.Errors.Item


 
Zikurat ©   (2014-12-29 13:40) [17]

В моем запросе у меня  Errors.Count = 0

Событие InfoMessage не возникает ((

Я так и не понял как ты получаешь эти предупреждения...


 
Zikurat ©   (2014-12-29 13:40) [18]

В моем запросе у меня  Errors.Count = 0

Событие InfoMessage не возникает ((

Я так и не понял как ты получаешь эти предупреждения...


 
sniknik ©   (2014-12-30 07:54) [19]

> В моем запросе у меня  Errors.Count = 0
режим асинхронный, прошу заметить... получить сразу после выполнения запроса не получится. нужно подождать, проверить статус исполнения (чего там конкретно не помню уже).

> Событие InfoMessage не возникает (
у тебя ошибка в коде, выше уже несколько раз про датасеты говорил, не могут они глючного рекордсета разобрать, а глючный первым идет... валится до всех событий. у меня на "командах" нет разбора, нет ошибки, есть событие.


 
Zikurat ©   (2014-12-30 16:03) [20]


> режим асинхронный, прошу заметить.

конечно, я это учитываю


> нужно подождать, проверить статус исполнения

естественно, возникает ADOConnectionExecute, в котором всегда все ок, то есть esOk


> е могут они глючного рекордсета разобрать, а глючный первым
> идет.

да, откуда и вопрос - где ты взял эти сообщения аля:

8153 : Внимание! Значение NULL исключено в статистических или других операциях SET.

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


procedure TForm1.ADOConnection1ExecuteComplete(Connection: TADOConnection;
 RecordsAffected: Integer; const Error: Error;
 var EventStatus: TEventStatus; const Command: _Command;
 const Recordset: _Recordset);
begin
 if EventStatus = esOk then
   MyAdoDataset.Recordset := Recordset;
...


то получим исключение, что рекордсет "not open"


 
sniknik ©   (2014-12-30 23:03) [21]

> Я их ни разу не видел.
ты не читаешь ответы. игнорируешь их. не правишь код. правильно... откуда их тебе увидеть.

> Если в том же ADOConnectionExecute попробовать присвоить рекордсет в существующий датасет:
НЕ ПРИСВАИВАЙ ДАТАСЕТУ! датасет ГЛЮЧИТ с "кривым" рекордсетом.


 
Zikurat ©   (2014-12-31 09:49) [22]

Мы ходим по кругу... ты можешь простым языком объяснить из какого свойства события ты берешь ошибку предупреждение про set


 
sniknik ©   (2014-12-31 15:07) [23]

sniknik ©   (26.12.14 22:16) [16]


 
Zikurat ©   (2014-12-31 16:19) [24]

Ну что же такое ... я же тоже написал что errors.count у меня равен нулю!!

Естественно я дождался окончания выполнения запроса возник ado connection execute complete !! И через 10 секунд в errors пусто после этого.


 
sniknik ©   (2014-12-31 17:48) [25]

> я же тоже написал что errors.count у меня равен нулю!!
ошибка в коде.

> Естественно я дождался окончания выполнения запроса возник ado connection execute complete !! И через 10 секунд в errors пусто после этого.
скорее всего курсор локальный, при нем ошибки не передаются... вроде, не  помню. вот сообщения (print) так точно, разбирался этим как то одно время.


 
Zikurat ©   (2014-12-31 18:17) [26]


> ошибка в коде.

не понял я этой фразы.


> вроде, не  помню. вот сообщения (print) так точно, разбирался
> этим как то одно время.
>

Николай... у меня ощущение, что ты надо мной издеваешься :((... Можешь ты привести хоть какой-то намек на кусок кода, который может в моем запросе:

SET NOCOUNT ON;

declare @max_timestamp timestamp;

select @max_timestamp=max(timestamp) from
 (
     select max(timestamp) timestamp from Table1
     union
     select max(timestamp) timestamp from Table2
 ) t;

select @max_timestamp;


Отловить твою ошибку:

8153 : Внимание! Значение NULL исключено в статистических или других операциях SET.

У меня НЕ получается! Все события говорят о том, что запрос исполнился, все хорошо. Единственная "фигня" - это что рекордсет присвоить нельзя. Но вот твоего текста об ошибки я нигде не вижу.


 
sniknik ©   (2014-12-31 19:54) [27]

> Николай... у меня ощущение, что ты надо мной издеваешься :((...
а мне что ты.

> Можешь ты привести хоть какой-то намек на кусок кода, который может в моем запросе:
запрос <> код.

> Единственная "фигня" - это что рекордсет присвоить нельзя.
РЕКОРДСЕТ можно, но не ДАТАсету, сколько раз повторять. вот так sniknik ©   (26.12.14 22:03) [15] в твоем случае присвоить получится, хотя даст ошибку в нормальном, т.к. без проверки а второго там не будет.


 
Zikurat ©   (2015-01-01 17:50) [28]

слушай, можешь показать компонент, участок кода, свойство или что-то такое, откуда можно извлечь ошибку

"8153 : Внимание! Значение NULL исключено в статистических или других операциях SET."

при моем запросе?


 
sniknik ©   (2015-01-02 12:57) [29]

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

на тупое повторение первого поста иногда разбавленное словами "не получается", я давно уже не даю кода. не адекватно ведешь беседу с моей точки зрения.
хочешь результата, вернись к началу и ПОПЫТАЙСЯ.


 
Zikurat ©   (2015-01-02 13:28) [30]

Ясно,  спасибо хотя бы за что-то



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2015.09.10;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.051 c
15-1417524936
И. Павел
2014-12-02 15:55
2015.09.10
Уроки по программированию


2-1392643424
Дмитрий СС
2014-02-17 17:23
2015.09.10
Редактор очень широких текстов.


11-1259936563
Егорка
2009-12-04 17:22
2015.09.10
Сервисы + Win7


15-1421340945
Fox
2015-01-15 19:55
2015.09.10
Ранжирование игр на Google Pkay


15-1415447176
dmk
2014-11-08 14:46
2015.09.10
Про переменные





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский