1 (edited by m.prokhachev 2018-08-12 11:08:07)

Topic: Ломаем TDataSet :)))

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;

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

Re: Ломаем TDataSet :)))

отсюда 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 и ниибёт...

3 (edited by m.prokhachev 2018-08-12 13:19:08)

Re: Ломаем TDataSet :)))

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

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

Re: Ломаем TDataSet :)))

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

Re: Ломаем TDataSet :)))

m.prokhachev wrote:

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

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

Dmitry.

Re: Ломаем TDataSet :)))

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 за одну и ту же дату, при этом дат может быть сколь угодно...

Re: Ломаем TDataSet :)))

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


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

Dmitry.

Re: Ломаем TDataSet :)))

DriveSoft wrote:

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

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

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

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

Re: Ломаем TDataSet :)))

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

10 (edited by m.prokhachev 2018-08-15 18:25:35)

Re: Ломаем TDataSet :)))

Всё, отбой воздушной тревоги по посту номер 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'ов вручную из скрипта...

Re: Ломаем TDataSet :)))

И все-таки продолжаем ломать 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...

Re: Ломаем TDataSet :)))

К сожалению скрипты не поддерживают объявление типов.


Вы используете динамические массивы MeasurementData и frxDBDataset_MeasurementData, для которых необходимо указывать размер с помощью процедуры SetLength, в вашем случае неизвестно количество датасетов, поэтому попробуйте так

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) + '...');
      SetLength(MeasurementData, Length(MeasurementData)+1);
      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));

  // готовим данные для передачи в отчет
  SetLength(frxDBDataset_MeasurementData, Length(MeasurementData));
  max := Length(MeasurementData)-1;
  for i := 0 to max 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;
Dmitry.

13 (edited by m.prokhachev 2018-08-31 21:00:03)

Re: Ломаем TDataSet :)))

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

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);