Быстрый поиск разницы файлов

Ну собственно это история одной маленькой победы, которые происходят обычно у ИТишников каждый день 😉

Предыстория: при работе скрипта по заливке данных в БД из файла произошло зависание сервера. Скрипт работал в несколько потоков с одним файлом. Потому определить на каком именно месте файла произошла остановка не представлялось возможным. Удалять уже залитое в БД и стартовать скрипт заново — не вариант, скрипт и так работал двое суток, и терять их снова — ну так себе решение.

Решение №1. «В лоб». Ну думаю доработаю скрипт так, что если данные уже есть, то просто пропускаем. Т.е. перед вставкой выполняем проверку функцией вида:

function lsYet($period,$ls){
    global $db;
  $res=false;
  $sql="select posting_addresses.id from posting_addresses
    inner join ls on ls.id=posting_addresses.ls
    where 
    posting_addresses.period='$period' and ls.ls='$ls'";
    $query = $db->connection->prepare($sql);
    $query->execute();
    while ($row99 = $query->fetch()) {
      $res=true;
    };        
 return $res;
}

И без проблем дозальем то, чего нет в БД. Да не тут то было, оказывается операция select в этом случае весьма дорогостоящая, и т.к. в БД записей порядка 600тыс, и индексы проставлены на ls и period корректно, но всёж скорость проверки крайне низкая, и т.о. скорость «дозалития» сокращается с двух суток до суток. Ну что собственно не устраивает.

Решение №2. Вдумчивое. Решил было выгрузить ключевые строки (лицевой счет) в файлы: файл ls_in_base.txt — лицевые счета которые уже в БД и ls_all.txt — файл со всеми лицевыми счетами, которые должны быть в БД, отсортировав их командой sort:

sort ls_in_base.txt > ls_in_base.txt
sort ls_all.txt > ls_all.txt

Далее воспользуемся Linux командой comm, вычленив уникальные записи файла ls_all которые не содержаться в файле ls_in_base:

comm -23 ls_all.txt ls_in_base.txt > no_in_base.txt

И далее уже в скрипте вместо проверки наличия лс в БД при помощи запроса, проверяем наличие лс в БД при помощи in_array:

$no_in_base_ls=file_get_contents("no_in_base.txt");
$no_in_base_arr=explode("\n",$no_in_base_ls);
...
if (in_array($ls, $no_in_base_arr)==true){
...
заливаем данные в БД
...
}

В результате скорость увеличилась в разы, и БД дозалить удалось в течении часа

PostgreeSQL: формат даты

Для форматирования даты в стоку нужного формата в PGSQL предусмотрена функция to_char(дата,формат). Использовать в запросе можно примерно так:

select 
comments.comment,
to_char(comments.dt, 'DD.MM.YYYY HH24:MI:SS'),
ls.ls,
users.login
from comments
inner join ls on ls.id=comments.lsid
inner join users on users.id=comments.userid
where ls.ls='1234' order by dt desc limit 10

При этом поддерживаются следующие паттерны форматирования:

ПаттернОписание
HHчас дня (01-12)
HH12час дня (01-12)
HH24час дня (00-23)
MIминуты (00–59)
SSсекунды (00–59)
MSмилисекунды (000–999)
USмикросекунды (000000–999999)
FF1десятые доли секунды (0-9)
FF2сотая доля секунды (00-99)
FF3милисекунлы (000–999)
FF4десятая часть миллисекунды (0000-9999)
FF5сотая доля миллисекунды (00000-99999)
FF6микросекунды (000000–999999)
SSSS, SSSSSсекунды после полуночи (0-86399)
AM , am , PM или pmмеридианный индикатор (без периодов)
A.M. , a.m. , P.M. или p.m.меридианный индикатор (с периодами)
Y,YYYгод (4 и более цифры)с запятой
YYYYгод (4 и более цифры)
YYYпоследние 3 цифры года
YYпоследние 2 цифры года
Yпоследняя цифра года
IYYYISO 8601 год с номером недели (4 или более цифр)
IYYпоследние 3 цифры года по стандарту ISO 8601 год с номером недели
IYпоследние 2 цифры года по ISO 8601 номер недели
Iпоследняя цифра года недели по ISO 8601
BC , bc , AD или adиндикатор эры (без периодов)
B.C. , b.c. , A.D. или a.d.индикатор эры (с периодами)
MONTHполное название месяца в верхнем регистре (с пустыми кнопками до 9 символов)
Monthполное название месяца с заглавными буквами (с пустыми кнопками до 9 символов)
monthполное название месяца в нижнем регистре (с пустыми кнопками до 9 символов)
MONсокращенное название месяца в верхнем регистре (3 символа на английском языке,локализованная длина варьируется)
Monсокращенное название месяца с заглавными буквами (3 символа на английском языке,локальная длина варьируется)
monсокращенное название месяца в нижнем регистре (3 символа на английском языке,локализованная длина варьируется)
MMномер месяца (01-12)
DAYполное название дня в верхнем регистре (с пустыми кнопками до 9 символов)
Dayполное название дня с заглавной буквы (пустое,до 9 символов)
dayполное название дня в нижнем регистре (с пустыми кнопками до 9 символов)
DYсокращенное название дня в верхнем регистре (3 символа на английском языке,локализованная длина варьируется)
Dyсокращенное название дня с заглавными буквами (3 символа на английском языке,локальная длина варьируется)
dyсокращенное название дня в нижнем регистре (3 символа на английском языке,локализованная длина варьируется)
DDDдень года (001-366)
IDDDдень года с нумерацией недель ISO 8601 (001-371;день 1 года-понедельник первой недели ISO)
DDдень месяца (01-31)
Dдень недели, с воскресенья ( 1 ) по субботу ( 7 )
IDISO 8601 день недели, с понедельника ( 1 ) по воскресенье ( 7 )
Wнеделя месяца (1-5)(первая неделя начинается в первый день месяца)
WWномер недели года (1-53)(первая неделя начинается в первый день года)
IWномер недели года с нумерацией недель ISO 8601 (01-53;первый четверг года приходится на неделю 1)
CCвек (2 цифры)(XXI век начинается с 2001-01-01 гг.)
JЮлианская дата (целое число дней с 24 ноября 4714 г. до н.э. в местную полночь; см. Раздел B.7 )
Qquarter
RMмесяц римскими цифрами в верхнем регистре (I-XII;I=январь)
rmмесяц в нижнем регистре римскими цифрами (i-xii;i=январь)
TZАббревиатура часового пояса в верхнем регистре (поддерживается только в to_char )
tzаббревиатура часового пояса в нижнем регистре (поддерживается только в to_char )
TZHсмещение зоны в часах
TZMсмещение зоны в минутах
OFсмещение часового пояса от UTC (поддерживается только в to_char )

Перенос работающего сайта WordPress с Mysql на PostgreeSQL

Задача: перенести рабочий, наполненный постами, сайт на движке WordPress с БД MySQL на PostgreeSQL

Инструментарий:

1) pgloader — утилита для переноса данных из баз данных MySQL в PostgreeSQL

Особенности о которые спотыкался:

  • для успешного переноса необходимо чтобы пользователь в терминале под которым осуществляется перенос имел максимальные права в БД и имел доступ «без пароля» для входа в консоль pgsql
  • при переносе создается схема с именем БД, а не заливается по умолчанию в схему public. Что в принципе для нашей ситуации даже будет плюсом.
  • Перед переносом, БД уже должна быть создана в PostgreeSQL

Синтаксис:

pgloader mysql://login:password@host/db_name pgsql:///db_name

2) Плагин PG4WP который на «лету» исправляет запросы к MySQL на PostgreeSQL

Особенности:

  • работает только при «чистой» установке. Т.е. WordPress должен быть штатно проинсталлирован с нуля
  • Инсталляция корректно работает только до версии WordPress 5.9
  • Плагин заброшен, последнее обновление — 3 года назад

Итак, общий алгоритм переноса рабочего WordPress получается такой:

  1. Переносим при помощи pgloader БД в отдельную схему
  2. Заново устанавливаем WordPress 5.9 с плагином PG4WP
  3. Удаляем из схемы public wp_posts (это если только статьи переносим) и переносим её из схемы созданной pgloader в public
  4. Пробуем обновить штатно WordPress

Точно понадобится:

-- перенос таблицы из отдной схемы в другую
ALTER TABLE wp_base.wp_users
SET SCHEMA public 

Удаление всех таблиц в схеме:

-- удаляем таблицы
DO $$ DECLARE
  r RECORD;
BEGIN
  FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP
    EXECUTE 'DROP TABLE ' || quote_ident(r.tablename) || ' CASCADE';
  END LOOP;
END $$;
-- удаляем последовательности
DO $$ DECLARE
    r RECORD;
BEGIN
    FOR r IN (SELECT relname FROM pg_class where relkind = 'S') LOOP
        EXECUTE 'DROP SEQUENCE IF EXISTS ' || quote_ident(r.relname) || ' CASCADE';
    END LOOP;
END $$;

Поменять схему всех таблиц:

DO $$ DECLARE
  r RECORD;
BEGIN
  FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'старая_схема') LOOP
    EXECUTE 'ALTER TABLE старая_схема.' || quote_ident(r.tablename) || ' SET SCHEMA новая_схема ';
  END LOOP;
END $$;

В принципе пока вполне рабочая схема

PostgreeSQL: ошибка вызова sha256 со значением столбца в качестве параметра

Функция sha256 создаёт хэш. Однако есть нюанс — в качестве входящего параметра она принимает тип bytea. Соответственно если вы вызовете что-то вроде:

select sha256('aa')

То результат будет получен. Однако при попытке выполнить что-то типа:

select sha256(txt) from users;

Получите ошибку вида:

ERROR:  function sha256(character varying) does not exist
LINE 1: select sha256(txt) from users;
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883

Т.е. не соответствие типов. Как быть? На лету преобразуем тип текст в тип bytea:

select sha256(txt::bytea) from users;

Insert или Update если не уникальная запись

Маленькая напоминалка . Синтаксис pgsql чуть отличается от mysql.

Задача: если запись не уникальна в таблице, то просто обновить дату последнего обновления.

MySQL:

INSERT INTO users_sessions(user_id, ssid) VALUES(10, "1") ON DUPLICATE KEY UPDATE last_updated=now()

PostgreeSQL:

insert into users_sessions (user_id,ssid) values (10,'1') on conflict(ssid) do update set last_updated=now();

Уникальным должен быть объявлен столбец ssid

1 2