![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
[D2010] Магия мыши в TSQLQuery + DateTimePicker [MySQL]
megavoid © (2010-12-14 20:13:00)Здравствуйте, Мастера!
Столкнулся с такой проблемой:
Есть форма, на ней stringgrid, sqlquery, datetimepicker. Юзеры выбирают дату в datetimepicker, в onChange конструируется select с нужной датой, выполняется через query, в цикле (query.next) читаю ответ от query и заполняю stringgrid нужными значениями. Всё вроде бы шоколадно, но есть одно но - если выбирать значение в picker'e мышью (sic!), то query при попытке интерпретировать ответ (.asString, .asInteger) выдаёт ошибку: Query: Field 'N1' not found. Не всегда выдаёт, произвольно, один раз из десяти-пятнадцати. Если же выбирать дату в пикере стрелочками, всё прекрасно, никакой ошибки. Более того, по событию FormCreate отрабатывает datetimepickerchange(self), и при запуске проги в гриде тоже всё прекрасно отображено.
Усугубляет разброд в моём мозге то, что поидее сообщения вообще выдаваться не должно бы (try..except), а оно выдаётся :)
Выглядит же этот безобразный цикл примерно так:
try
Query.Open;
for j := 0 to query.RecordCount-1 do begin
// здесь идут блоки s := fieldbyname .asstring; .asinteger; итд
try
S := query.FieldByName('N1').AsString;
except
end;
// здесь мы эти s пихаем в stringgrid
Query.Next;
Application.ProcessMessages;
end;
finally
Query.Close;
end;
При возникновении этого глюка отладчик вылетает в окно CPU, а не на строку между try..except. Стэк вызовов процедур получается такой:
KERNELBASE.RaiseException
DatabaseError
DatabaseErrorFmt
Dataset.FieldByName
стоп указывает на инструкцию leave
перед ней call dword ptr [$7555116c] - что там у меня по этому адресу - Готт его знает, перешёл туда - там jnbe, перед ней ещё пара инструкций и нули, много-много нулей :) Увы, мой ассемблер закончился в досе на уровне mov ah, 9 int 21h, так что мне это ровным счётом ничем не помогло :)
Вопрос: что же всё-таки её вызывает, ошибку эту, если мышкой выбирать дату? Пока вопрос решён чисто по-русски, то есть юзеры (~80 человек) не выбирают мышкой, но мучит меня совесть и интерес профессиональный, что ж за фигня такая :)
Плиз хелп, всем заранее спасибо!
megavoid (2010-12-15 12:13:00) [61]ну, хоть на расстрел набежали, и то радует
повторюсь, проблема не в том, что что-то не работает, а в том, что всё работает.
@sniknik [20]
сделал сразу же, результат в [8]
> указывать * в запросе есть плохой тон. sql движок это напрягает.
что же мне указывать, если мне все поля нужны? перечислять их все через запятую?
вот тот самый метод полностью
procedure TForm1.FillGrid(Grid: TZColorStringGrid; Req: String);
var
i, j, QCash, zal, rating: integer;
S, Trainer, Clients, Task, RatingStr: string;
begin
InitGridLines;
Qry.Close;
Qry.SQL.Clear;
Qry.SQL.Add(Req + ';');
// showmessage(Qry.sql.text); // !!
// for j := 0 to qry.Params.Count-1 do showmessage(qry.params[i].Name + qry.params[i].value); // !!
try
Qry.Open;
// if Qry.IsEmpty then showmessage('Пусто'); // !!
for j := 0 to Qry.RecordCount-1 do begin
// for i := 0 to 9 do showmessage(qry.fields[i].FieldName + qry.fields[i].asstring); // !!
try
S := TimeToStr(Qry.FieldByName('dtime').AsDateTime);
except
on E: Exception do
ShowMessage(E.Message + #13#10 + qry.SQL.Text);
end;
try
i := StrToInt(Copy(S,0,Pos(':',S)-1)) - 6;
except
end;
QCash := Qry.FieldByName('cash').AsInteger;
Zal := Qry.FieldByName('zal').AsInteger;
Rating := Qry.FieldByName('rating').AsInteger;
Trainer := FIO2ShortFIO( Qry.FieldByName('trainer').AsString );
Clients := Qry.FieldByName('client').AsString;
Task := Qry.FieldByName('task').AsString;
if Rating = 0 then RatingStr := ''
else RatingStr := ' (' + IntToStr(Rating) + '*)';
// one or more tasks at one time
if Grid.Cells[i, Zal] = '' then begin
if QCash = 3 then
Grid.Cells[i, Zal] := Trainer + '[' + Task + ']' + RatingStr
else
Grid.Cells[i, Zal] := Trainer + '[' + Task + ']' + #13#10 + Clients;
end else begin
if QCash = 3 then
Grid.Cells[i, Zal] :=
Grid.Cells[i, Zal] + #13#10#13#10 + Trainer + '[' + Task + ']' + RatingStr
else
Grid.Cells[i, Zal] :=
Grid.Cells[i, Zal] + #13#10#13#10 + Trainer + '[' + Task + ']' + #13#10 + Clients;
//Grid.CellStyle[i, Zal].WordWrap := True;
end;
if QCash = 3 then
Grid.CellStyle[i, Zal].BGColor := clSetka // сетка
else {if Zal = 1 then}
Grid.CellStyle[i, Zal].BGColor := clZal; // 1 зал
Qry.Next;
Application.ProcessMessages;
end;
except
on E: Exception do
ShowMessage(E.Message + #13#10 + Qry.SQL.Text);
// Qry.Close; // finally
end;
Qry.Close;
AutoSizeGrid(Grid);
end;
вызывается он так:
procedure TForm1.DateTimePicker1Change(Sender: TObject);
begin
FillGrid(StringGrid1, 'SELECT * FROM z WHERE ddate = ' + fd2sql(datetimepicker1.DateTime));
end;
fd2sql (c) Белов Владимир
function TForm1.FD2SQL(ADateEdit : TDateTime) : string;
var Year, Month, Day : word;
s : string;
begin
if ADateEdit = 0 then Result:='NULL'
else begin
DecodeDate(ADateEdit, Year, Month, Day);
s:='';
s:=s+IntToStr(Year)+'-';
if Month < 10 then s:=s+'0';
s:=s+IntToStr(Month)+'-';
if Day < 10 then s:=s+'0';
s:=s+IntToStr(Day);
// s:=s+' 00:00:00';
Result:=chr($27)+s+chr($27);
end;
end;
вот, так есть. всё работает.
ошибка возникает, если выбрать дату в tdatetimepicker мышкой.
sniknik (2010-12-15 12:12:00) [62]> но и Trunc - тоже бывает глючит. Я заводил тему, как-то.
Trunc вряд ли, а вот это -
> Q.Params[i]. Value:=
возможно. из-за того, что при не заданности типа ADO к примеру берет его из присваиваемого "варианта", а присваиваемый "вариант" получается из приведенного значения, т.е. integer-а в случае с Trunc
> но, если
> TDateTime(Trunc( datetimepicker.date))
> то ошибку ODAC пишет, дескать я хочу время, а мне суют число
видимо с точки зрения оптимизатора подобное приведение типа не существенно (все одно числа туда сюда автоматом приводятся)
> но, на деле вот так вот работает, а иначе - нет.
иначе можно еще тип параметру явно указать, перед значением, и значит "автоопределением" из варианта.
Anatoly Podgoretsky (2010-12-15 11:26:00) [63]> 12 (15.12.2010 10:45:28) [28]
Это Trunc - тоже бывает глючит, там же нет места для глюка, это выполняет
процессор, он просто обрезает дробную часть. Если бы был глюк по этой части
уже стоял гигантский крик в Интернет.
12 (2010-12-15 10:45:00) [64]
> sniknik © (15.12.10 09:53) [24]
>
> 12 © (15.12.10 08:44) [21]
> > Query.parambyname('DATE').asDAtetime / или ).value / :
> = datetimepicker.date;
> дата в датапикере тоже типа дататайм, т.что могут быть проблемы,
> если где оставить "огрызки времени", причем неожиданно,
> и непонятно (например в XP работает, в win 7 почему то
> нет. сталкивался с подобным).
> лучше, для гарантии, делать например так
> asDateTime:= Trunc(datetimepicker.date);
Согласен.
эт я для простоты
но и Trunc - тоже бывает глючит. Я заводил тему, как-то.
try
Q.SQL.Text := SQL;
s := '';
for i := 0 to Q.ParamCount-1 do
begin
case Q.Params[i].DataType of
ftDateTime: Q.Params[i].AsDateTime := P[i];
else Q.Params[i].Value := P[i];
end;
s := s + Q.Params[i].Name + '=' + Q.Params[i].AsString + #13#10;
end;
Q.Open;
теперь пишу так:
TDateTime(StartOfTheDay( datetimepicker.date))
хотя не понятно, ведь
function StartOfTheDay(const AValue: TDateTime): TDateTime;
begin
Result := Trunc(AValue);
end;
но, если
TDateTime(Trunc( datetimepicker.date))
то ошибку ODAC пишет, дескать я хочу время, а мне суют число
с другой стороны, число и есть время.
но, на деле вот так вот работает, а иначе - нет.
Anatoly Podgoretsky (2010-12-15 10:09:00) [65]> Ega23 (15.12.2010 10:06:26) [26]
Купил, или ты думаешь, что это он возмущается по поводу ворованой,
взломаной?
Тогда это вообще двойной лох.
Ega23 (2010-12-15 10:06:00) [66]
> Не, его просто развели как лоха на покупку Дельфи.
Ха. Ха. Ха.
Купил.
Anatoly Podgoretsky (2010-12-15 10:03:00) [67]> sniknik (15.12.2010 09:53:24) [24]
Не, его просто развели как лоха на покупку Дельфи.
sniknik (2010-12-15 09:53:00) [68]12 © (15.12.10 08:44) [21]
> Query.parambyname('DATE').asDAte time/ или ).value / := datetimepicker.date;
дата в датапикере тоже типа дататайм, т.что могут быть проблемы, если где оставить "огрызки времени", причем неожиданно, и непонятно (например в XP работает, в win 7 почему то нет. сталкивался с подобным).
лучше, для гарантии, делать например так
asDateTime:= Trunc(datetimepicker.date);
> Не это ты лох.
в этом нет сомнений, как только начинаются "доказательства" типа "у меня все правильно", только почему то не работает... и "что вы придираетесь к показанному коду" (а к чему еще?) "проблема не в нем, а в том про что я для вас фантазирую" вместо твердых "вот что есть, вот как делается, при этом при вот таких условиях, вот такая ошибка".
Плохиш (2010-12-15 09:50:00) [69]
> megavoid (15.12.10 00:26) [16]
> > Не факт. Впрочем, это твоё дело.
> ну тогда мне остаётся лишь признать, что создатели дельфи
> - полнейшие лохи
Ламер-дерьмокодер детектед.
Anatoly Podgoretsky (2010-12-15 09:10:00) [70]
> ну тогда мне остаётся лишь признать, что создатели дельфи
> - полнейшие лохи, ведь они и сами об этом не знают!
Не это ты лох.
И все то у тебя есть, вот только мы об этом узнаем потом.
12 (2010-12-15 08:44:00) [71]
> for j := 0 to qry.Params.Count-1 do showmessage(qry.params[i].
> Name + qry.params[i].value);
> гм, ничего не показывается
а для чего было написано? :)
> 'SELECT * from table WHERE date=' + format2sql(datetimepicker.
> date) + ';');
Query.sql.text := 'SELECT * from table WHERE date= :DATE';
Query.parambyname('DATE').asDAtetime / или ).value / := datetimepicker.date;
Query.open;
sniknik (2010-12-15 08:02:00) [72]> Единственный совет, к которому я не прислушался
как бы тыкать носом в проигнорированное тут тоже никому не нужно, но один раз по такой настойчивой просьбе...
Ega23 © (14.12.10 22:24) [6]
Ещё раз, возьми и сделай следующее:
try
S := query.FieldByName('N1').AsString;
except on E: Exception do
ShowMessage(E.Message + #13#10 + query.SQL.Text);
end;
И после этого станет ясно, где там и чего not found.
+
> Query.Add('SELECT * from table WHERE date=' + format2sql(datetimepicker.date) + ';');
1 указывать * в запросе есть плохой тон. sql движок это напрягает.
2 параметры.
> приведите мне, пожалуйста, пример, когда TSqlQuery в этом случае вернёт мне НЕ ВСЕ нужные записи.
а что ты знаешь о серверных курсорах? запрос тут совершенно ни при чем. достаточно самого факта его использования. оно просто так работает. асинхронно.
megavoid (2010-12-15 04:50:00) [73]В этом проекте 20 форм и более 100 запросов. SQLQuery один. Все они (запросы) for i < recordcount всегда отрабатывают нормально (open/execsql). Кроме этого случая с выбором даты в пикере мышью.
megavoid (2010-12-15 04:37:00) [74]@sniknik
> > впрочем, делу это не помогает нисколечко :(а чем это должно
> помочь? могло бы, если бы прислушивался к советам, а так.
> . нет.
Единственный совет, к которому я не прислушался - это переделать цикл с предусловия for на постусловие while. Всё остальное из ответов я проделал с прилежностью ученика церковно-приходской школы, и привёл все полученные результаты в топике.
Query.Clear;
Query.Add('SELECT * from table WHERE date=' + format2sql(datetimepicker.date) + ';');
Query.Open;
try
/* приведите мне, пожалуйста, пример, когда TSqlQuery в этом случае вернёт мне НЕ ВСЕ нужные записи. Может быть, я с этим просто не столкнулся. Обычно возвращается в среднем порядка 100 записей. */
for i := 0 to Query.RecordCount-1 do begin
// here are we're getting info from answer
Query.Next;
end;
finally
Query.Close;
end;
Во всех примерах использования dbExpress без отображения в dbgrid, для простого формирования запроса к БД указан именно этот путь - делаем запрос, open, читаем записи next, закрываем sqlquery. Если база не возвращает результат - используем execsql. Можно, конечно, и переделать цикл на while not eof, если recordcount врёт, но с чего ему врать? В той же документации указано, что RecordCount содержит количество записей в наборе данных однонаправленного датасета.
sniknik (2010-12-15 02:09:00) [75]> впрочем, делу это не помогает нисколечко :(
а чем это должно помочь? могло бы, если бы прислушивался к советам, а так.. нет.
по второй ссылке про однонаправленные датасеты, косвенное подтверждение словам Ega23 про неопределенность RecordCount и все проблемы с этим связанные. т.к. однонаправленные могут быть только с серверным курсором, а для серверных это актуально... и то, что "через раз" тоже, т.к. успевает - работает, а чуть "повезло" быстрее сработать - глюк.
> ау, эксперты, откликнитесь пожалуйста!!!
тебе уже сказали, или следуй советам, и переделывай, даже не понимая причин, или... "это твоё дело", как бы переубеждать "насильно" это тебе нужны другие эксперты.
megavoid (2010-12-15 00:26:00) [76]> Не факт. Впрочем, это твоё дело.
ну тогда мне остаётся лишь признать, что создатели дельфи - полнейшие лохи, ведь они и сами об этом не знают!
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/SqlExpr_TSQLQuery.html
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/5datasetusingunidirectionalresultsets_xml.html
впрочем, делу это не помогает нисколечко :(
ау, эксперты, откликнитесь пожалуйста!!!
Ega23 (2010-12-14 23:59:00) [77]
> Поэтому все данные будут у меня сразу после TSQLQuery.Open
Не факт. Впрочем, это твоё дело.
megavoid (2010-12-14 23:24:00) [78]@Ega23
Хочется выть и биться об стол головой :)
Где вы у меня TDBGrid и DataSet увидели??? у меня TStringGrid и TSQLQuery!!!
Поэтому все данные будут у меня сразу после TSQLQuery.Open, а потом вручную данные из Query я вношу в StringGrid с нужным заказчику форматированием.
Ega23 (2010-12-14 23:14:00) [79]
> мне непонятно, почему грид правильно берёт все данные из
> якобы "закрытого набора".
Во, кстати. если ты внимательно просмотришь код TDBGrid, то ты увидишь, что он берёт строго те данные, которые в данный момент отображаются на экране.
Это возвращаясь к моему предыдущему посту.
Представь, что выборка у тебя на 100 записей. На экране в данный момент ты видишь первые 20. Остальные могут уже находиться в DataSet, могут ещё продолжать "подсасываться". А могут подсосаться тогда, когда ты грид проскроллишь.
Ega23 (2010-12-14 23:10:00) [80]
> Поэтому я не вижу здесь принципиальной разницы между for
> и while
Ещё раз: это тебе дружеский совет. Следовать ему или нет - это твоё дело.
Но RecordCount будет точно определён, когда данные полностью зафетчатся. До конца. А гарантии, что это произойдёт строго после DataSet.Open - нет. Это зависит от настроек конкретной реализации DataSet и от драйвера доступа.