Ошибка запроса MySQL «Недопустимое значение часа», вызывающая проблемы с циклом и записью в скрипте Google Apps - PullRequest
3 голосов
/ 30 сентября 2019

Я плохо разбираюсь ни в одном из языков, поэтому, пожалуйста, потерпите меня. Я пытаюсь вытащить полную таблицу из 100 строк из удаленной базы данных MySQL в Google Sheet. Мне удалось разобраться во всех проблемах, которые у меня были с этим, но, наконец, я застрял. Кажется, ошибка «Недопустимое значение часа», которую я получаю из запроса SQL во время цикла, является основной проблемой.

Один из столбцов в базе данных MySQL - это «продолжительность», и, к сожалению, он может содержать длительности большечем 23:59:59. У меня есть доступ только для вызова процедуры и я не могу вносить изменения в таблицу. Я получаю

«недопустимое значение часа»

, когда строка достигает длительности более 24 часов (например, 70:00:00) . Я попытался упростить ситуацию, используя try-catch, чтобы пропустить ошибку и продолжить запись в Google Sheet, но затем я получаю

"количество столбцов в данных, не соответствующее числустолбцы в диапазоне. Данные имеют X, но диапазон имеет Y "

ошибка в последней строке sheet.getRange ().

Я также не могу понять, какпередать несколько операторов при выполнении запроса MySQL. Я пытался понять addBatch и пару других вещей, но это становится слишком сложным для меня. Может быть, есть более простое решение с запросом, или, может быть, это единственное решение, потому что оно могло бы работать, если бы я мог также добавить запрос CONCAT после запроса CALL, чтобы преобразовать столбец «duration» в строку перед тем, как данные попадут в цикл.

Код ниже был обновлен, чтобы включить решение:

function getData3(query, sheetName) {

  //MySQL (MariaDB) connection and statements.
  var user = '';
  var userPwd = '';
  var url = 'jdbc:mysql://remote.server.example.com/database_name';
  var conn = Jdbc.getConnection(url, user, userPwd);
  var stmt2 = conn.createStatement();
  stmt2.setMaxRows(100);
  var rs2 = stmt2.executeQuery('CALL spdAdminGetPIREPS(api_key)');
  //Logger.log(rs2)

  //Function to convert raw binary data to string to be used for durations >23:59:59.
  function byteArrToString(byteArr){
    return Utilities.newBlob(byteArr).getDataAsString();
  }

  //Setting up spreadsheet, results array, and cell range.
  var doc = SpreadsheetApp.openById("id");
  var sheet = doc.getSheetByName("sheet_name");
  var results = [];
  var cell = doc.getRange('a1');
  var row = 0;

  //Loop to get column names.
  cols = rs2.getMetaData();
  colNames = [];
  for (i = 1; i <= cols.getColumnCount(); i++ ) {
    //Logger.log(cols.getColumnName(i));
    colNames.push(cols.getColumnName(i));
  }
  results.push(colNames);

  //Loop to get row data, catch type errors due to duration >23:59:59 and fix it.
  var rowCount = 1;
  while(rs2.next()) {
    curRow = rs2.getMetaData();
    rowData = [];
    for (i = 1; i <= curRow.getColumnCount(); i++) {
      try {
      rowData.push(rs2.getString(i));
      } catch (e){
      var bytes = rs2.getBytes(i);
      rowData.push(byteArrToString(bytes)); //Pushes converted raw binary data as string using function defined above.
      //Logger.log(JSON.stringify(rs2.getBytes(i))); //To see the raw binary data returned by getBytes() for durations >23:59:59 that throw an error.
        continue;
      }
    }
    results.push(rowData);
    rowCount++;
  }

  //Write data to sheet.
  sheet.getRange(1, 1, rowCount, cols.getColumnCount()).clearContent();
  sheet.getRange(1, 1, rowCount, cols.getColumnCount()).setValues(results);

  //Close result set, conn, and statement.
  //Logger.log(results);
  rs2.close();
  stmt.close();
  conn.close();
}

Я знаю два отдельных утверждения, и все выглядят нелепо, нокажется, что они работают, потому что я больше не получаю ошибку «нет базы данных» с запросом. Более простой однострочный JDBC-коннектор не работал для меня, поэтому текущий формат для подключения к серверу MySQL (Mariadb).

Если в таблице нет длительностей более 24 часов, код работаети успешно записывает всю таблицу в Google Sheet.

Для суммирования:

Если я не использую try-catch, цикл останавливается с ошибкой. Если я использую try-catch и continue, я получаю ошибку несоответствия количества столбцов.

Конечная цель - вызвать процедуру и записать всю таблицу в Google Sheet. Пропуск проблемных ячеек, с которыми я, вероятно, могу работать, но я бы определенно хотел получить все данные. Я мог бы упустить что-то тривиальное здесь, но любое направление или помощь будут оценены. Заранее спасибо!

ОБНОВЛЕНИЕ: Я нашел этот вопрос, на который дан ответ, но не могу понять, как использовать его в моем случае. Я думаю, что если я смогу пропустить несколько запросов, я смог бы отправить запрос CONCAT после запроса CALL, чтобы преобразовать столбец "duration" из Datetime (я полагаю) в string.

ОБНОВЛЕНИЕ 2: @ Решение TheMaster для try-catch помогло пропустить проблемную ячейку и продолжить запись остальных. Я хотел бы найти способ преобразовать все длительности (весь столбец или те >23:59:59) в string, чтобы захватить все данные.

ОБНОВЛЕНИЕ 3: На основании предложений @ TheMaster, использование getInt() вместо getString() частично работает, возвращая час, но не минуты (например, возвращает 34, если продолжительность 34:22:00). Нужен способ конвертировать при использовании getString().

ОБНОВЛЕНИЕ 4 (отредактировано): Используя getBytes(), возвращаемые значения:[51,52,58,52,56,58,48,48] для 34:48:00[51,55,58,48,48,58,48,48] для 37:00:00[56,55,58,48,48,58,48,48] для 87:00:00[49,53,49,58,51,53,58,48,48] для 151:35:00Что значит:[48,49,50,51,52,53,54,55,56,57,58] соответствует [0,1,2,3,4,5,6,7,8,9,:]. Как я могу включить это преобразование? Используя getLong(), возвращаемые значения:34 для 34:48:00, преобразовано в длительность -> 816:0037 для 37:00:00, преобразовано в длительность -> 888:0087 для 87:00:00, преобразовано в длительность -> 2088:00

ФИНАЛ ОБНОВЛЕНИЯ: @ Модифицированный ответ TheMaster решил проблему, получив необработанные двоичные данные и преобразовав их в строку на длительность >23:59:59. Код выше был обновлен, чтобы отразить все модификации;это работает как написано выше.

Ответы [ 2 ]

3 голосов
/ 01 октября 2019

В настоящее время вы используете коннектор MySQL, и даже если значение TIME может быть от '-838: 59: 59.999999' до '838: 59: 59.999999', драйвер MySQL выдает исключение при получении значениятип ВРЕМЯ не в диапазоне 0-24 часа. Это может иметь смысл при использовании Resultset.getTime (i), когда это не так, но не при использовании Resultset.getString (i).

Это можно отключить с помощью noDatetimeStringSync=true, поэтому изменив URL на jdbc:mysql://remote.server.example.com?noDatetimeStringSync=true

Отказ от ответственности: я один из сопровождающих java-драйвера MariaDB, но я бы порекомендовал использовать драйвер MariaDB с сервером MariaDB (и MySQL с сервером MySQL). Вы бы избежали этой проблемы:)

Кстати, вы можете напрямую установить базу данных в URL jdbc:mysql://remote.server.example.com/database?noDatetimeStringSync=true, избегая запроса.

2 голосов
/ 01 октября 2019

Если вы хотите пропустить сбой getString(), это должно быть просто:

try {
      rowData.push(rs2.getString(i));
      } catch (e){
      rowData.push("");//Keeps array straight
        continue;
      }

Если вы хотите преобразовать столбец времени в строку, вам нужно использовать CAST(time AS char) или CONCAT('',time) по запросу CREATE_PROCEDURE, используемому для создания spdAdminGetPIREPS

В качестве альтернативы, вы можете получить необработанные двоичные данные, используя resultSet.getBytes() и изменитьвернитесь к string через blob, используя Utilities:

function byteArrToString(byteArr){
 return Utilities.newBlob(byteArr).getDataAsString();
}

Используйте его как

var bytes = rs2.getBytes();
rowData.push(byteArrToString(bytes));

Если вы можете напрямую получить BLOB-объект, онбудет легче получить appsScriptBlob

var jdbcBlob = rs2.getBlob();
var blob = jdbcBlob.getAppsScriptBlob();
rowData.push(blob.getDataAsString());
jdbcBlob.free();
...