Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Помогите оптимизировать код (https://javascript.ru/forum/misc/72427-pomogite-optimizirovat-kod.html)

Зосимов 28.01.2018 17:56

Помогите оптимизировать код
 
Всем привет.
есть функция, которая отправляет данные на сервер из локальной Web SQL
function SendPost() { 
    
    db.transaction(function (tx) {
    tx.executeSql("SELECT keyNumber, itemNumber, curDate FROM items", [], function(tx, results) {                    
    
    if(results.rows.length > 0) {
                for(var i = 0; i < results.rows.length; i++) {  
                        var someArray = [results.rows.item(i).keyNumber, 
                results.rows.item(i).itemNumber,results.rows.item(i).curDate];
                        addArray(someArray);                     
                   }
            }

        });
    });

    function addArray(arr) {

        $.ajax({
           url: 'http://www.test.ru/add_to_base.php',
            dataType: 'text',
            type: 'post',
            contentType: 'application/x-www-form-urlencoded',
            data: ({knb:arr[0], inb:arr[1], cdt:arr[2]}),
            success: function( data, textStatus, jQxhr ){
                console.log(data);
            },
            error: function( jqXhr, textStatus, errorThrown ){
                   if(jqXhr.status&&jqXhr.status==400){
                    alert("Ошибка!: " + jqXhr.responseText); 
               }
               else if(jqXhr.status&&jqXhr.status==500){
                    alert("500 - ошибка подключения к серверу");
               }
               else{
                   alert("Something went wrong");
               }
            }
        }); 
 }

код работает, но есть вопросы:

1) как лучше оптимизировать, что бы не вызывать каждый раз функцию addArray(). или это нормально - на каждую запись отправлять POST?

2) в случае ошибки, как остановить выполнение кода? например при ошибке 500. Так как цикл уже запустился, то прервать работу SendPost() уже не получается :(

буду рад любым советам

laimas 28.01.2018 19:36

Цитата:

Сообщение от Зосимов
это нормально - на каждую запись отправлять POST?

Лучше сразу все результаты выборки отправить на сервер. Статус 500, это не ошибка подключения.

PS. Ваш код означает, что клиент валит сервер своими запросами, от этого 500. Если каждую запись отправлять отдельно, то делать это нужно только после того, как сервер ответит на предыдущий запрос.

Зосимов 29.01.2018 07:03

Цитата:

Сообщение от laimas (Сообщение 476458)
Лучше сразу все результаты выборки отправить на сервер. Статус 500, это не ошибка подключения.

PS. Ваш код означает, что клиент валит сервер своими запросами, от этого 500. Если каждую запись отправлять отдельно, то делать это нужно только после того, как сервер ответит на предыдущий запрос.

так в том то и вопрос - как потравить все результаты?

laimas 29.01.2018 07:47

Удалить функцию addArray, перенеся Ajax запрос на место строк 7-11, передавая серверу результат запроса к БД как JSON.

На сервере декодировать и выполнить.

Зосимов 29.01.2018 09:08

Цитата:

Сообщение от laimas (Сообщение 476492)
передавая серверу результат запроса к БД как JSON.

не могли бы привести пример?

laimas 29.01.2018 09:33

Не знаю какой пример приводить, так как это связка клиент-сервер. Но коли сервер оперировал одной строкой данных, а теперь получит всю выборку, то серверный скрипт придется все равно править. Поэтому можно отослать как есть, и на сервере оперировать именами полей локальной SQL таблицы - keyNumber, itemNumber, curDate, а не knb, inb, cdt.

В этом случае:

db.transaction(function (tx) {
    tx.executeSql("SELECT * FROM items", [], function(tx, results) {                   
        if(results.rows.length > 0) {
            $.ajax({
                //...
                data: 'key='+JSON.stringify(results.rows),
                //...
            })
        }
     });
});


На сервере вся выборка, это json_decode($_POST['key']). Если по каким-то причинам этого не желательно, то:

db.transaction(function (tx) {
    tx.executeSql("SELECT * FROM items", [], function(tx, results) {                   
        if(results.rows.length > 0) {
            
            for(var i=0, rows=[]; i<results.rows.length; i++) rows.push({
                knb:results.rows.item(i).keyNumber, 
                inb:results.rows.item(i).itemNumber, 
                cdt:results.rows.item(i).curDate
            });
            
            $.ajax({
                //...
                data: 'key='+JSON.stringify(rows),
                //...
            })
        }
     });
});


Многое зависит от операций с данными на сервере.

Зосимов 29.01.2018 13:39

laimas, спасибо. буду пробовать.
На сервере строки пишутся в БД в том же виде

laimas 29.01.2018 13:51

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

INSERT INTO tbl (filed, filed, filed) VALUES (val, vall, val), (val, vall, val), (val, vall, val), ... с учетом соглашений и макс. размера пакетной записи.

Зосимов 29.01.2018 14:44

Цитата:

Сообщение от laimas (Сообщение 476521)
Если сервер пишет в базу, то тем более выгоднее сразу все вставить, одним запросом многострочной вставкой. А для этого хватит и просто одних значений (без ключей), главное чтобы клиент передавал их в нужном порядке. На сервере эти данные приводятся к запросу:

INSERT INTO tbl (filed, filed, filed) VALUES (val, vall, val), (val, vall, val), (val, vall, val), ... с учетом соглашений и макс. размера пакетной записи.

примерно так записывал когда приходила строка данных на сервер.
но сейчас на сервер приходит массив такого типа:
(index):349 Array
(
 [key] => [{"knb":"111111111111","inb":"222222222222","cdt":"2018-01-29 17:20:27"},{"knb":"qqqqqqqqqqqq","inb":"666666666666","cdt":"2018-01-29 17:21:28"},{"knb":"qqqqqqqqqqqq","inb":"777777777777","cdt":"2018-01-29 17:21:28"}]
)
теперь нужно считать строки на сервере и записывать каждую?

laimas 29.01.2018 15:10

NSERT INTO tbl (field_name, field_name, field_name) VALUES (value, value, value) - это вставка в таблицу одной строки. Можно циклом обойти массив, вставляя таким образом отдельно строки. Но выгоднее сделать один запрос вставив сразу несколько строк, если БД это позволяет. Синтаксис запроса тот же самый, только перечислений вставляемых данных будет равно их числу, с указанием их через запятую:

NSERT INTO tbl (field_name, field_name, field_name) VALUES (value, value, value), (value, value, value), ...

Но при такой записи нужно учитывать максимально разрешенный размер пакетной записи. В MySQL по умолчанию он равен 16 МБ. Если данных гарантированно меньше этого объема, то подготавливаем запрос и выполняем его. В противном случае данные разбиваются на несколько запросов для многострочной вставке. Можно узнать разрешенный размер пакетной записи запросом 'SHOW VARIABLES LIKE "max_allowed_packet"'. Запрос вернет размер в байтах.

Из $_POST нужно еще получить данные:

$data = json_decode($_POST['kye'], 1);


А далее данные из массива $data подготовить для запроса учитывая, что данные должны быть обработаны и соответственно представлены (int или string). Если сервер использует оригинальное расширение MySQL, то придется все делать ручками. В случае если mysqli или PDO, то используя подстановки для подготовленного запроса, а "безопасность" запроса обеспечит сам SQL драйвер.

Зосимов 29.01.2018 15:52

Цитата:

Сообщение от laimas (Сообщение 476521)
Из $_POST нужно еще получить данные:
$data = json_decode($_POST['kye'], 1);

А далее данные из массива $data подготовить для запроса учитывая, что данные должны быть обработаны и соответственно представлены (int или string). Если сервер использует оригинальное расширение MySQL, то придется все делать ручками. В случае если mysqli или PDO, то используя подстановки для подготовленного запроса, а "безопасность" запроса обеспечит сам SQL драйвер.

я решил вставлять записи вот так
for ($i = 0; $i < count($data); $i++){
$success = mysql_query("INSERT INTO ....  VALUES ('".$data[$i]["knb"]."', '".$data[$i]["inb"] .....

записей будет около 3000 строк за раз. думаю это не так много

laimas 29.01.2018 15:55

Цитата:

Сообщение от Зосимов
записей будет около 3000 строк за раз. думаю это не так много

Очень плохо, что так, ибо дергать базу запросами, это плохо, а 3000 запросов, это слишком плохо. Более того, ваш запрос это 100%-ная дыра в безопасности.

Зосимов 30.01.2018 06:20

Цитата:

Сообщение от laimas (Сообщение 476546)
Очень плохо, что так, ибо дергать базу запросами, это плохо, а 3000 запросов, это слишком плохо. Более того, ваш запрос это 100%-ная дыра в безопасности.

:blink: так а что вы порекомендуете?
я проверил, запросы выполняются не так долго, несколько секунд. Так что не пойму - что именно плохого?

laimas 30.01.2018 07:45

Цитата:

Сообщение от Зосимов
не пойму - что именно плохого?

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

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

Контекст вашей темы, это оптимизация. А что такое по вашему оптимизация - красиво выполнить действия на клиенте? А на сервере как получится?

Если СУБД не позволяет записать в базу набор данных в рамках одного запроса, это плохо, но придется выполнять массу запросов. Но если позволяет, то почему же вы не думаете об оптимизации? Я ведь показывал что нужно сделать, да даже если бы и не показал, вы, думая об оптимизации, просто обязаны изучить возможности СУБД, дабы сделать запись оптимальной.

Это вы обязаны знать - http://fi2.php.net/manual/ru/security.database.php.
Это очень желательно знать - http://fi2.php.net/manual/ru/book.filter.php.
Это http://fi2.php.net/manual/ru/book.mysqli.php или это http://fi2.php.net/manual/ru/book.pdo.php нужно использовать вместо этого http://fi2.php.net/manual/ru/book.mysql.php.

Используя оригинальное расширение MySQL, вы обязаны сами экранировать данные подставляемые в запрос функцией mysql_real_escape_string(). При этом ожидаемые данные типа integer можно приводить к данному типу, и при этом помещать их в кавычки совсем не требуется.

Допустим, если в ключах knb и inb ожидаем integer, а в cdt строку, то данные первых двух ключей приводим к integer, а последнего экранируем функцией mysql_real_escape_string() и обрамляем кавычками. Подготовив таким образом данные для запроса, делаем запрос:

if($data = json_decode($_POST['kye'])) {
    //каждое вложение $data при таком декодировании будет объектом
    //подготавливаем запрос
    $sql = 'INSERT INTO FROM table (...) VALUES ' . implode(',', array_map(function($v) {
               return '(' . (int)$v->kbn . ',' . (int)$v->inb . ',"' . mysql_real_escape_string($v->cdt) . '")'; 
    }, $data));
    //выполняем запрос
    mysql_query($sql);    
}


При подготовке запроса можно не просто приводить данные к типу и экранировать, а производить их фильтрацию - если (int)$v->kbn или (int)$v->inb ноль, а это не должно быть, значит липовые данные, завершаем работу. Дату ли содержит $v->cdt или "Привет Федя!" ведь тоже не проблема проверить. То есть, либо if((int)$v->kbn) ..., либо используем класс Filter.

А в mysqli и PDO все, кроме фильтрации в контексте "то ли пришло?", можно возложить на sql-драйвер, указав типы и используя подстановки. Драйвер подготовит запрос корректно экранируя и подставив данные в запрос. Запрос будет выглядеть как

'INSERT INTO FROM table (...) VALUES (?,?,?),(?,?,?),(?,?,?),...'

На исполнение отдается этот запрос и массив данных - все вложенные массивы помещаются в один одномерный. Декодирование JSON при этом должно возвращать массив, указывая функции json_decode вторым параметром true/1.

И почитайте о различиях разбора в РНР строковых значений помещенных в одинарные и двойные кавычки.

Зосимов 30.01.2018 08:18

laimas, все понял. Спасибо большое! Вы мне очень помогли.


Часовой пояс GMT +3, время: 00:40.