51

(29 replies, posted in Russian)

Так я не пойму... Я ведь тоже передаю переменные в отчет из программы... и да, не-числа у меня заключены в апострофы, но при этом в тексте отчета собственно текст, не обрамленный апострофами... И что не так-то у вас?

52

(12 replies, posted in Russian)

итак, корректно работающий код оказался таким:

var
  id_Sensors: TDataSet;
  i, maxIndex: integer;
  MeasurementData: array of TDataSet;
  frxDBDataset_MeasurementData: array of TfrxDBDataSet;
  ...
  SQLQuery('SELECT id FROM Sensors WHERE id_GroupPileList=' + id_GroupPileList, id_Sensors);  // в id_Sensors получили все id датчиков группы
  ...
  i := 0;
  SetLength(MeasurementData, 1);
  while not id_Sensors.Eof do
    begin
     SQLQuery('SELECT Sensors.SerialNumber, Sensors.SubGroupID, strftime("%d.%m.%Y",SensorsData.measurementDate) as "measurementDate", round(SensorsData.measurement_F,3) as "measurement_F", round(SensorsData.measurement_t,3) as "SensorsData.measurement_t", round(SensorsData.currentPressure, 3) as "currentPressure", SensorStatusList.SensorStatus FROM SensorsData LEFT JOIN Sensors ON Sensors.id = SensorsData.id_Sensors LEFT JOIN SensorStatusList ON SensorStatusList.id = SensorsData.id_SensorStatusList WHERE SensorsData.id_Sensors=' + id_Sensors.FieldByName('id').asString + ' ORDER BY SensorsData.measurementDate', MeasurementData[i]);
     id_Sensors.Next;
     SetLength(MeasurementData, Length(MeasurementData) + 1);
     i := i + 1;
   end;
  SetLength(MeasurementData, Length(MeasurementData) - 1);
  ...
  SetLength(frxDBDataset_MeasurementData, Length(MeasurementData));
  maxIndex := Length(frxDBDataset_MeasurementData) - 1;
  for i := 0 to maxIndex do
    begin
      frxDBDataset_MeasurementData[i] := TfrxDBDataset.Create(MeasurementData[i]);
      frxDBDataset_MeasurementData[i].UserName := 'MeasurementData_'+IntToStr(i);
      frxDBDataset_MeasurementData[i].CloseDataSource := True;
      frxDBDataset_MeasurementData[i].OpenDataSource := True;
      frxDBDataset_MeasurementData[i].DataSet := MeasurementData[i];
    end;
  ...
  for i := 0 to maxIndex do
    frmMain.frxReport.DataSets.Add(frxDBDataset_MeasurementData[i]);
  ...
  for i := 0 to maxIndex do
    frxDBDataset_MeasurementData[i].DataSet.Close;
  ...
  for i := 0 to maxIndex do
    frxDBDataset_MeasurementData[i].Free;

  SetLength(frxDBDataset_MeasurementData, 0);
  SetLength(MeasurementData, 0);
Petr wrote:

День добрый!
1.Если сделать login главной формой то форма Main после ввода верного пароля не открывается, при этом форма login закрывается.
2.Если сделать переход на форму login из другой формы то всё работает.
Подскажите почему первый вариант не работает?

Я тоже, кстати, заметил, что Form.ShowModal не со всеми формами корректно работает... Поэтому перешёл только на Form.Show, но, чувствую, что это как-то плохо влияет на пул памяти, отведенный на программу.

кстати, Дмитрий, чем отличается Form.Hide от Form.Close?

55

(17 replies, posted in Russian)

Хм... Вроде бы проблема решилась так:

procedure form_buttonWithActionSearch_OnClick (Sender: TObject; var Cancel: boolean);
begin
  form.TableGrid.dbFilter := '';
end;

то есть в OnClick кнопки с действием "поиск", которая на самом деле должна возвращать грид в исходное состояние (как при первом показе формы), надо прописать обнуление конструкции WHERE в автоматически формируемом SQL-запросе
Потестирую какое-то время, если обнаружится недостаток - отпишусь.

56

(17 replies, posted in Russian)

sibprogsistem wrote:

просто поместите на форму кнопку "поиск"
в поиске задайте заполнение грида
скройте кнопку
повесьте клик на OnShow
никаких других параметров не нужно

Хреновый костыль, в общем... Так как все равно требуется указать компонент, участвующий в поиске. Обычно я указываю сам грид на форме... Но если два-три раза подряд нажать, то в итоге в гриде одна запись и остается... Причем обычно самая первая по id в таблице БД.

Вот если бы можно было НЕ указывать компоненты, участвующие в поиске - тогда было бы здорово... Может, Дмитрий повесит туда по умолчанию какую-нить опцию, которая не будет встраивать конструкцию WHERE <условие>?..

57

(12 replies, posted in Russian)

И все-таки продолжаем ломать TDataSet...
Попытался запустить код типа:

var
  MeasurementData: array of TDataSet;
  frxDBDataset_MeasurementData: array of TfrxDBDataSet;
  ...
  SQLQuery('SELECT id FROM Sensors WHERE id_GroupPileList=' + id_GroupPileList, id_Sensors);
  ...
  // получаем информацию о всех замерах по каждому конкретному датчику в группе, каждый датчик - в отдельном DataSet'е
  // заодно формируем в id_List список id_Sensors для запроса среднего арифметического по трем датчикам по каждой дате
  i := 0;
  while not id_Sensors.Eof do
    begin
      ShowMessage('сейчас будет SQL-запрос №' + IntToStr(i) + '...');
      SQLQuery('SELECT Sensors.SerialNumber, Sensors.SubGroupID, strftime("%d.%m.%Y",SensorsData.measurementDate) as "measurementDate", SensorsData.measurement_F, SensorsData.measurement_t, round(SensorsData.currentPressure, 3) as "currentPressure", SensorStatusList.SensorStatus FROM SensorsData LEFT JOIN Sensors ON Sensors.id = SensorsData.id_Sensors LEFT JOIN SensorStatusList ON SensorStatusList.id = SensorsData.id_SensorStatusList WHERE SensorsData.id_Sensors=' + id_Sensors.FieldByName('id').asString + ' ORDER BY SensorsData.measurementDate', MeasurementData[i]);
      ShowMessage('SQL-запрос №' + IntToStr(i) + ' прошёл!');
      id_Sensors.Next;
      i := i + 1;
    end;
  max := i; // максимальное количество элементов в массиве DataSet'ов
  ShowMessage('max_i = ' + IntToStr(max));

  // готовим данные для передачи в отчет
  for i := 0 to max - 1 do
    begin
      frxDBDataset_MeasurementData[i] := TfrxDBDataset.Create(MeasurementData[i]);
      frxDBDataset_MeasurementData[i].UserName := 'MeasurementData_'+IntToStr(i);
      frxDBDataset_MeasurementData[i].CloseDataSource := True;
      frxDBDataset_MeasurementData[i].OpenDataSource := True;
      frxDBDataset_MeasurementData[i].DataSet := MeasurementData[i];
    end;

в общем, на втором проходе цикла while выскакивает ошибка с текстом "Variant or safe array index out of bounds"
на Delphi-ресурсах рекомендуют сделать так:

type
  TDataSetArray = array of TDataSet;
  TfrxDBDatasetArray = array of TfrxDBDataSet;
  ...
var
  MeasurementData: TDataSetArray;
  frxDBDataset_MeasurementData: TfrxDBDataSetArray;

однако MVD стал требовать begin перед type... и опять я в тупике... никак не получается сделать универсальный код, а в лоб по простому не очень хочется из-за того, что это будет выглядеть как быдло-код)
Опять же на Delphi-ресурсах еще обсуждают идею с типом Variant, однако его все равно надо типировать в блоке type...

Дмитрий, тут большинство не сильны в программировании баз данных, поэтому свои примеры желательно разжевывать тщательнее) это я еще могу понять, что к чему, а вот большинство - не очень...
Евгений, для тупого копирования содержимого одной таблицы в другую вам надо выполнить запрос такого вида:

INSERT INTO table2  (fieldname1, fieldname2, ...) SELECT fieldname1, fieldname2, ... FROM  table1

где table1 - название таблицы, откуда копируется, table2 - название таблицы, куда копируется, а fieldname1, fieldname2, ... - это название полей данных (таким образом вы можете выбрать какие поля хотите переносить в новую таблицу из старой).
Вы можете перенести ВСЕ поля со всеми данными следующим запросом:

INSERT INTO table2 SELECT * FROM table1

Если надо перенести данные с каким-то условием, то после FROM table1 используйте конструкцию WHERE <условие>, где <условие> - ваше условие выборки данных.

59

(29 replies, posted in Russian)

collagen wrote:

Прописал скрипт - апострофы остались.

А скинь скриншот сформированного отчета, где эти апострофы имеются...

Если я правильно понимаю, то тебе тогда надо изменить процедуру передачи переменных в отчет, вместо:

procedure VariableToReport ();
var
    vrbl: string;  
begin
    Form1.frxReport.Variables.Clear;
    Form1.frxReport.Variables[' ' + 'Components'] := Null;
    vrbl := frmTexZavd.Memo1.Text;
    Form1.frxReport.Variables['Memo1'] := ''''+vrbl+'''';
    vrbl := frmTexZavd.Memo2.Text;
    Form1.frxReport.Variables['Memo2'] := ''''+vrbl+'''';
    vrbl := frmTexZavd.Memo3.Text;
    Form1.frxReport.Variables['Memo3'] := ''''+vrbl+'''';
end;

прописать так:

procedure VariableToReport ();
begin
    Form1.frxReport.Variables.Clear;
    Form1.frxReport.Variables[' ' + 'Components'] := Null;
    Form1.frxReport.Variables['Memo1'] := frmTexZavd.Memo1.Text;
    Form1.frxReport.Variables['Memo2'] := frmTexZavd.Memo2.Text;
    Form1.frxReport.Variables['Memo3'] := frmTexZavd.Memo3.Text;
end;

потому что по моему маленькому еще опыту конструкция вида

Form1.frxReport.Variables['Memo1'] := ''''+vrbl+'''';

нужна лишь в том случае, если переменная vrbl имеет символы, которые FastReport может принять как математические операции, например, - или / или * или +. Ну и я позволил себе избавиться от переменной vrbl, оптимизировав код, ибо она реально лишняя)

60

(12 replies, posted in Russian)

Всё, отбой воздушной тревоги по посту номер 6 в этой теме. Покурил маны, потыкал в SQLiteStudio свою БД разными запросами, в итоге нужный результат получился следующим запросом:

SELECT strftime('%d.%m.%Y',Date) as "mDate", AVG(P) as "avgP" FROM SensorsData WHERE (id_Sensors=.. OR id_Sensors=.. OR id_Sensors=..) GROUP BY Date ORDER BY Date

важным оказалось иметь именно mDate вместо Date, иначе ORDER BY Date не срабатывал
P.S. Однако я все равно хотел бы видеть в следующих версиях MVD возможность создания DataSet'ов вручную из скрипта...

61

(3 replies, posted in Russian)

Прикольно) насколько я помню еще со школыных уроков информатики, всегда указывалась сначала строка, в которой поиск производится, а потом только строка, которая ищется) Но спасибо) Сам дурак, как обычно)

62

(3 replies, posted in Russian)

Что самое интересное - и при реализации собственной функции Pos

function MyPos(ps,st:string):byte;
var n,m,i,j,p: byte;
    s: string;
begin
  p := 0;
  n := ord(st[0]);
  m := ord(ps[0]);
  for i := 1 to n-m+1 do
    if st[i] = ps[1] then
      begin
        s := '';
        for j := i to i+m-1 do
          s := s + st[j];
        if s=ps then
          begin
            p := i;
            break;
          end;
      end;
  Result := p;
end;

тот же самый результат... я уже ни черта не понимаю...

Следующий код не работает ни в одном проекте, в котором я пробовал запустить его на исполнение:

var s: string;
...
s := '345,345';
ShowMessage(Pos(s, ','));

ShowMessage показывает "0" и все тут! менял строку поиска с запятой на цифры и числа - монопенисуально, 0 и идите к черту!
а вроде как Pos - стандартная функция MVD... предлагает писать свою реализацию Pos?)))

64

(12 replies, posted in Russian)

Если бы можно было передавать в отчет массив и синхронизировать вывод элементов массива с элементами DataSet'а, то вопросов бы у меня не возникало...

65

(12 replies, posted in Russian)

DriveSoft wrote:

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

Также опишите, каким образом сенсоры объединены в группы?

Ох, там нормализованная БД, по факту информация собирается с трех таблиц - SensorsData, Sensors и GroupPileList, связь такая GroupPileList <- Sensors <- SensorsData через ключи, впрочем, увидите сами по схеме БД в проекте. У датчиков отдельно имеется SerialNimbers и также идентификатор <GroupID>.<SubGroup> - это такой шифр на этикетке в месте установки группы датчиков на кабелях связи самих датчиков. GroupID принадлежит таблице GroupPileList, SubGroup - таблице Sensors

проект тут - https://dropmefiles.com/PDFz3

66

(12 replies, posted in Russian)

DriveSoft wrote:
m.prokhachev wrote:

Вообще, хотелось бы краткого курса по созданию собственного DataSet'а в коде программы, ибо есть необходимость создать свой DataSet...

MVD к сожалению не позволяет создавать собственные DataSet.
С какой целью это необходимо сделать вам?

есть таблица в БД, назовем SensorsData, в которой содержатся записи вида

Date | id_Sensors | t | F | P 

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

2018-06-27 | 17 | 22 | 2850 | 0,300
2018-06-27 | 18 | 21 | 2840 | 0.290
2018-06-27 | 19 | 22 | 2845 | 0.294
...
2018-01-02 | 17 | -3 | 2832 | 0.150
2018-01-02 | 18 | -3 | 2839 } 0.154
2018-01-02 | 19 | -3 | 2825 | 0.137

id_Sensors - это ключ на другую таблицу, где находятся данные по собственно датчикам, каждый из которых имеет свою присущую ему информацию. датчики объединены в группы по три, то есть id_Sensors = 17, 18 и 19 относятся к одной группе датчиков.
я пытаюсь получить данные по изменению показаний датчика по времени (по датам записи показаний) по каждому датчику и вычислить среднее значение по группе, чтобы отобразить это на графике в отчете.
сейчас я вывожу серию данных по каждому датчику в отдельный DataSet SQL-запросом, однако не могу создать DataSet, в котором бы присутствовали Date и среднее значение по группе датчиков за дату замера. то есть создаваемый мной DataSet должен иметь всего два поля - Date и avgP.
я думал над SQL-запросом, который бы создавал вычисляемое поле, но я так и не допёр, как вычислить среднее арифметическое от P трех id_Sensors за одну и ту же дату, при этом дат может быть сколь угодно...

67

(12 replies, posted in Russian)

Вообще, хотелось бы краткого курса по созданию собственного DataSet'а в коде программы, ибо есть необходимость создать свой DataSet...

68

(12 replies, posted in Russian)

Вообще, насколько я понимаю, пп. 1 и 2 первого поста можно решить конструкцией SELECT .. JOIN LEFT ... в SQLQuery() при заполнении SensorsSettingData: TDataSet... однако я пока еще не разобрался как из нескольких таблиц, связанных ключами, выбрать информацию сложным SQL-запросом/

UPD. Ну так и решил, да, конструкцией SELECT ... JOIN LEFT ...  WHERE, однако это не снимает вопросы, заданные в первом посте данной темы)

69

(12 replies, posted in Russian)

отсюда http://www.helloworld.ru/texts/comp/lan … /les35.htm:

Следующие методы позволяют Вам изменить данные, связанные с TTable:

procedure Append;

procedure Insert;

procedure Cancel;

procedure Delete;

procedure Edit;

procedure Post;

Все эти методы - часть TDataSet, они унаследованы и используются TTable и TQuery.

Всякий раз, когда Вы хотите изменить данные, Вы должны сначала перевести DataSet в режим редактирования. Как Вы увидите, большинство визуальных компонент делают это автоматически, и когда Вы используете их, то совершенно не будете об этом заботиться. Однако, если Вы хотите изменить TTable программно, Вам придется использовать вышеупомянутые функции.

Имеется a типичная последовательность, которую Вы могли бы использовать при изменении поля текущей записи:

Table1.Edit;

Table1.FieldByName(‘CustName’).AsString := ‘Fred’;

Table1.Post;

Первая строка переводит БД в режим редактирования. Следующая строка присваивает значение ‘Fred’ полю ‘CustName’. Наконец, данные записываются на диск, когда Вы вызываете Post.

При использовании такого подхода, Вы всегда работаете с записями. Сам факт перемещения к следующей записи автоматически сохраняет ваши данные на диск. Например, следующий код будет иметь тот же самый эффект, что и код показанный выше, плюс этому будет перемещать Вас на следующую запись:

Table1.Edit;

Table1.FieldByName(‘CustNo’).AsInteger := 1234;

Table1.Next;

Общее правило, которому нужно следовать - всякий раз, когда Вы сдвигаетесь с текущей записи, введенные Вами данные будут записаны автоматически. Это означает, что вызовы First, Next, Prior и Last всегда выполняют Post, если Вы находились в режиме редактирования.

попробовал нечто такое:

while not SensorsSettingData.Eof do
  begin
    SensorsSettingData.Edit;
    SensorsSettingData.FieldByName('id_Sensors').asString := SQLExecute('SELECT SerialNumber FROM Sensors WHERE id='+SensorsSettingData.FieldByName('id_Sensors').asString);
    SensorsSettingData.Next;
  end;

однако MVD заругался, мол, DataSet только read-only и ниибёт...

70

(12 replies, posted in Russian)

1)  пытаемся вместо внешнего ключа из первой таблицы подставить собственно значение поля таблицы, на которую ссылается этот ключ (вторая таблица - справочник):

// решаем вопрос выдачи в отчет статуса датчика по конкретному замеру, теперь в id_SensorStatusList разыменованый статус датчика
while not SensorsSettingData.Eof do
  begin
    SensorsSettingData.FieldByName('id_SensorStatusList').asString := SQLExecute('SELECT SensorStatus FROM SensorStatusList WHERE id='+SensorsSettingData.FieldByName('id_SensorStatusList').asString);
    SensorsSettingData.Next;
  end;

ругается на несовместимость типов SensorsSettingData.FieldByName('id_SensorStatusList') и получаемого по SQL-запросу строкового значения поля SensorStatus из таблицы SensorStatusList. есть ли вариант динамически менять тип поля в структуре TDataSet?
2) пытаемся вместо внешнего ключа первой таблицы проставить значения со второй таблицы (типы полей совпадают, оба integer):

while not SensorsSettingData.Eof do
  begin  // нет подстановки
    SensorsSettingData.FieldByName('id_Sensors').asString := SQLExecute('SELECT SerialNumber FROM Sensors WHERE id='+SensorsSettingData.FieldByName('id_Sensors').asString);
    ShowMessage(SensorsSettingData.FieldByName('id_Sensors').asString);
    SensorsSettingData.Next;
  end;

ShowMessage показывает, что нихрена не изменилось значение SensorsSettingData.FieldByName('id_Sensors') - как было взято из БД, таким и осталось, и пофиг на операцию присваивания. как так-то? все эти модификаторы представления asString, asInteger - зачем тогда?
3) и самое простое - пытаемся вычислить среднее значение по конкретному полю из нескольких записей (count и avgPressure - real):

count := 0; avgPressure := 0;
while not SensorsSettingData.Eof do
  begin
    avgPressure := avgPressure + SensorsSettingData.FieldByName('currentPressure').asFloat;
    count := count + 1;
    SensorsSettingData.Next;
  end;
avgPressure := avgPressure / count;

...работает!

71

(5 replies, posted in Russian)

DriveSoft wrote:
procedure Form1_Button1_OnClick (Sender: TObject; var Cancel: boolean);
var
    Results: TDataSet;
    sHost: string;
    id: integer;
begin
    SQLQuery('SELECT id, host FROM host', Results);
    while not Results.Eof do
    begin
        sHost := Results.FieldByName('host').asString;
        ID := Results.FieldByName('id').asInteger;

        Results.Next;
    end;

    Results.Close;
end;

а код такого вида

procedure Form1_Button1_OnClick (Sender: TObject; var Cancel: boolean);
var
    Results: TDataSet;
    sHost: string;
    id: integer;
begin
    SQLQuery('SELECT id, host FROM host', Results);
    while not Results.Eof do
    begin
        sHost := Results.FieldByName('host').asString;
        Results.FieldByName('id').asString := SQLExecute('SELECT MACadress FROM Table2 WHERE id_host='+Results.FieldByName('id').asString); 

        Results.Next;
    end;

    Results.Close;
end;

проканает?
то есть интересует возможность изменять значения элементов самого DataSet"а... он же ведь массив так или иначе?

72

(29 replies, posted in Russian)

collagen wrote:

И еще момент - при присвоении значения сриптом переменной Мемо

Employees.frxReport.Variables['Memo1'] := ''''+s+'''';

в отчете в начале и в конце ее текста появляются апострофы

ну так вам же и нужна строковая переменная, не? иначе FastReport не будет принимать значение передаваемой переменной как строку, а будет еще пытаться исчислять его

73

(46 replies, posted in Russian)

создай вторую таблицу для учета расходовании позиции - иначе ты не будешь знать историю расходования позиции, а ведь именно для этого БД и используется, по идее

Employees.frxReport.LoadFromFile(ExtractFilePath(Application.ExeName)+ReportFile);
Employees.frxReport.PrepareReport();
Filter.FileName := ChangeFileExt(GetTempFileName, sExt);
Employees.frxReport.Export(Filter);

получается, что вместо показа уже сформированного отчета при помощи Employees.frxReport.ShowReport или вместо открытия его в дизайнере при помощи Employees.frxReport.DesignReport можно экспортировать отчет в указанные выше форматы при помощи последних трех строк в приведенном куске кода
однако я бы поставил Filter.ShowDialog := True в принципе для всех вариантов экспорта, потому как стандартное имя шаблона отчета не должно фигурировать в названии файла с готовым отчетом. ну или проще для пакетной выдачи отчета формировать название файла теми же скриптами

на проект test2 - работает, правда, с непривычной логикой, интересно ,а на моем - опять не так, как могло быть