Pull to refresh
47
Вадим Петряев@ptr128

Архитектор ИС

0,4
Rating
40
Subscribers
Send message

не должна делаться ручками бухгалтера

Если руководство, в первую очередь финдир при согласовании с главбухом, приняли решения возложить некоторые операции необходимые для управленческого и финансового учёта на бухгалтера - будет делать, как миленький. Проверено неоднократно в McDonalds, P&G, Bunge, Siemens и многих других компаниях, где я так или иначе принимал участие во внедрении ERP.

Ключевое - сейчас.

Бухгалтерский учёт - лишь ничтожная часть планирования ресурсов предприятия. И то, что 1С уже очень давно (с 2013 года) пытается из бухгалтерии сделать ERP без кардинального пересмотра архитектуры - это их проблемы.

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

размер БД и отдельных таблиц в ней в петабайты и терабайты - это мерило успеха или криворукости?

Вам виднее:

Объемы - сотни GB, под террабайт базы бывали.

можно вернуться к Стругацким и их "Граду Обречённому"

В том же "Граде обреченном" немало героев, которых заботило далеко не только еда, питьё и жильё. Так что плохой пример.

Вы слышали о такой массовой болезни, как "прокрастинация"? это вот оно - реакция на отсутствие необходимости бороться.

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

Вместо hash запрос был загнан в merge join, что и упростило логику (не потребовалось считать хеши) и ускорило выполнение.

Вот так и не понял

Объясняю, очень сильно упрощая. Есть справочник вагонов:

CREATE TABLE cars (
  id integer PRIMARY KEY,
  accure_date date NOT NULL, -- дата акта получения
  dismiss_date date NULL, -- дата акта выбытия
  max_mileage integer -- максимальный пробег до планового ремонта
);

Есть рейсы вагонов:

CREATE TABLE hauls (
  car_id integer REFERENCES cars,
  out_date datetimetz NOT NULL, -- дата отправления
  haul_mileage integer NOT NULL -- пробег в рейсе
);

Задача - вывести для каждого вагона, доступного на текущую дату (между accure_date и dismiss_date) остаточный пробег до планового ремонта, как разницу между максимальным пробегом и пробегом во всех совершенных им рейсов с момента акта получения вагона. С учётом того, что в наличии есть вагоны, которые после покупки, взятия в аренду или получения их по доверенности ещё не совершали рейсов. И таких вагонов у крупных операторов ежедневно сотни.

Искать затем, что “когда-то я работал в компании” - это 10+ лет. Сейчас я с SQL работаю сильно меньше и примера готового все равно не имею.

Во-первых, сразу видим несоответствие между

Попросил ИИ подобрать пример

и

Объемы - сотни GB, под террабайт базы бывали.

Ну и, как следствие, у AI возникла галлюцинация и он вставил в таблицу metrics_nullable множество нулей, хотя их там быть не должно.

Для информации, у меня только одна таблица истории дислокаций вагонов больше двух терабайт. DWH же суммарно давно петабайт преодолел.

Во-вторых, добавлять сюда размер индексов некорректно, так как обсуждался размер кортежей.

Во-третьих, я писал "в некоторых случаях", из чего следует, что могу привести пример.

Пожалуйста
CREATE TABLE metrics_nullable (
    id        int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    value1    decimal(18,2),
    value2    decimal(18,2),
    value3    decimal(18,2),
    value4    decimal(18,2),
    value5    decimal(18,2),
    value6    decimal(18,2),
    value7    decimal(18,2),
    value8    decimal(18,2),
    value9    decimal(18,2),
    value10   decimal(18,2),
    value11   decimal(18,2),
    value12   decimal(18,2),
    value13   decimal(18,2),
    value14   decimal(18,2),
    value15   decimal(18,2),
    value16   decimal(18,2)
);
CREATE TABLE metrics_fixed (
    id        int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    value1    decimal(18,2) NOT NULL DEFAULT 0,
    value2    decimal(18,2) NOT NULL DEFAULT 0,
    value3    decimal(18,2) NOT NULL DEFAULT 0,
    value4    decimal(18,2) NOT NULL DEFAULT 0,
    value5    decimal(18,2) NOT NULL DEFAULT 0,
    value6    decimal(18,2) NOT NULL DEFAULT 0,
    value7    decimal(18,2) NOT NULL DEFAULT 0,
    value8    decimal(18,2) NOT NULL DEFAULT 0,
    value9    decimal(18,2) NOT NULL DEFAULT 0,
    value10   decimal(18,2) NOT NULL DEFAULT 0,
    value11   decimal(18,2) NOT NULL DEFAULT 0,
    value12   decimal(18,2) NOT NULL DEFAULT 0,
    value13   decimal(18,2) NOT NULL DEFAULT 0,
    value14   decimal(18,2) NOT NULL DEFAULT 0,
    value15   decimal(18,2) NOT NULL DEFAULT 0,
    value16   decimal(18,2) NOT NULL DEFAULT 0
);
INSERT INTO metrics_nullable (value1, value2, value3, value4, value5,
  value6, value7, value8, value9, value10, value11, value12, value13,
  value14, value15, value16)
SELECT
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END,
    CASE WHEN random() < 0.01 THEN (random()*1000+1)::int END
FROM generate_series(1, 1000000) AS gs(i);
INSERT INTO metrics_fixed (value1, value2, value3, value4, value5,
  value6, value7, value8, value9, value10, value11, value12, value13,
  value14, value15, value16)
SELECT
    COALESCE(value1, 0),
    COALESCE(value2, 0),
    COALESCE(value3, 0),
    COALESCE(value4, 0),
    COALESCE(value5, 0),
    COALESCE(value6, 0),
    COALESCE(value7, 0),
    COALESCE(value8, 0),
    COALESCE(value9, 0),
    COALESCE(value10, 0),
    COALESCE(value11, 0),
    COALESCE(value12, 0),
    COALESCE(value13, 0),
    COALESCE(value14, 0),
    COALESCE(value15, 0),
    COALESCE(value16, 0)
FROM metrics_nullable;
SELECT 'nullable' AS table_type, pg_size_pretty(pg_relation_size('metrics_nullable')) AS total_size
UNION ALL
SELECT 'fixed', pg_size_pretty(pg_relation_size('metrics_fixed'));

EXPLAIN ANALYZE
SELECT
    sum(value1), sum(value2), sum(value3), sum(value4), sum(value5),
    sum(value6), sum(value7), sum(value8), sum(value9), sum(value10),
    sum(value11), sum(value12), sum(value13), sum(value14), sum(value15), sum(value16)
FROM metrics_nullable;

Finalize Aggregate  (cost=27381.80..27381.81 rows=1 width=512) (actual time=79.031..80.915 rows=1.00 loops=1)
  Buffers: shared hit=5548
  ->  Gather  (cost=27381.39..27381.60 rows=2 width=512) (actual time=78.888..80.894 rows=3.00 loops=1)
        Workers Planned: 2
        Workers Launched: 2
        Buffers: shared hit=5548
        ->  Partial Aggregate  (cost=26381.39..26381.40 rows=1 width=512) (actual time=75.819..75.820 rows=1.00 loops=3)
              Buffers: shared hit=5548
              ->  Parallel Seq Scan on metrics_nullable  (cost=0.00..9714.67 rows=416667 width=80) (actual time=0.004..13.483 rows=333333.33 loops=3)
                    Buffers: shared hit=5548
Planning Time: 0.091 ms
Execution Time: 80.959 ms

EXPLAIN ANALYZE
SELECT
    sum(value1), sum(value2), sum(value3), sum(value4), sum(value5),
    sum(value6), sum(value7), sum(value8), sum(value9), sum(value10),
    sum(value11), sum(value12), sum(value13), sum(value14), sum(value15), sum(value16)
FROM metrics_fixed;

Finalize Aggregate  (cost=27439.68..27439.69 rows=1 width=512) (actual time=133.084..135.022 rows=1.00 loops=1)
  Buffers: shared hit=10310
  ->  Gather  (cost=27439.09..27439.40 rows=3 width=512) (actual time=132.909..134.984 rows=4.00 loops=1)
        Workers Planned: 3
        Workers Launched: 3
        Buffers: shared hit=10310
        ->  Partial Aggregate  (cost=26439.09..26439.10 rows=1 width=512) (actual time=129.484..129.485 rows=1.00 loops=4)
              Buffers: shared hit=10310
              ->  Parallel Seq Scan on metrics_fixed  (cost=0.00..13535.81 rows=322581 width=48) (actual time=0.005..10.629 rows=250000.00 loops=4)
                    Buffers: shared hit=10310
Planning Time: 0.091 ms
Execution Time: 135.067 ms

В-четвертых, проблема не столько в тупой выборке с суммированием, сколько в том, что NULL поле не требует поиска в индексе справочника, тогда как 0 - требует.

Поэтому имеем в результате
CREATE TABLE stations (
  id integer PRIMARY KEY, -- идентификатор станиции
  name varchar NOT NULL   -- наименование станции
);

INSERT INTO stations (id, name)
SELECT G.i, G.i::text
FROM generate_series(1,40000) G(i)
UNION ALL
SELECT 0, '-- отсутствует --';

DROP TABLE hauls_fixed;
DROP TABLE hauls_nullable;
CREATE TABLE hauls_nullable (
  id serial PRIMARY KEY,
  from_staion_id integer NOT NULL REFERENCES stations, -- станция отправления
  to_staion_id integer NOT NULL REFERENCES stations, -- станция назначения
  border_station_id integer NULL REFERENCES stations, -- станция пересечения границы
  export_station_id integer NULL REFERENCES stations, -- станция экспортного досмотра
  import_station_id integer NULL REFERENCES stations -- станция импортного досмотра
);

INSERT INTO hauls_nullable (from_staion_id, to_staion_id, border_station_id, export_station_id, import_station_id)
SELECT random()*39999+1, random()*39999+1,
  CASE WHEN random()<0.1 THEN random()*39999+1 END,
  CASE WHEN random()<0.1 THEN random()*39999+1 END,
  CASE WHEN random()<0.1 THEN random()*39999+1 END
FROM generate_series(1,1000000) G(i);

CREATE TABLE hauls_fixed (
  id serial PRIMARY KEY,
  from_staion_id integer NOT NULL REFERENCES stations, -- станция отправления
  to_staion_id integer NOT NULL REFERENCES stations, -- станция назначения
  border_station_id integer NOT NULL REFERENCES stations, -- станция пересечения границы
  export_station_id integer NOT NULL REFERENCES stations, -- станция экспортного досмотра
  import_station_id integer NOT NULL REFERENCES stations -- станция импортного досмотра
);

INSERT INTO hauls_fixed (from_staion_id, to_staion_id, border_station_id, export_station_id, import_station_id)
SELECT from_staion_id, to_staion_id, 
  COALESCE(border_station_id,0), COALESCE(export_station_id,0),
  COALESCE(import_station_id,0)
FROM hauls_nullable;

EXPLAIN ANALYZE
SELECT COALESCE(B.name,'-- отсутствует --'),
  COALESCE(E.name,'-- отсутствует --'),
  COALESCE(I.name,'-- отсутствует --')
FROM hauls_nullable H
LEFT JOIN stations B ON B.id=H.border_station_id
LEFT JOIN stations E ON E.id=H.export_station_id
LEFT JOIN stations I ON I.id=H.import_station_id;

Hash Left Join  (cost=3993.07..27297.13 rows=1000000 width=96) (actual time=19.255..387.291 rows=1000000.00 loops=1)
  Hash Cond: (h.import_station_id = i.id)
  Buffers: shared hit=6722
  ->  Hash Left Join  (cost=2662.05..23341.09 rows=1000000 width=14) (actual time=12.366..271.725 rows=1000000.00 loops=1)
        Hash Cond: (h.export_station_id = e.id)
        Buffers: shared hit=6291
        ->  Hash Left Join  (cost=1331.02..19385.04 rows=1000000 width=13) (actual time=6.237..173.591 rows=1000000.00 loops=1)
              Hash Cond: (h.border_station_id = b.id)
              Buffers: shared hit=5860
              ->  Seq Scan on hauls_nullable h  (cost=0.00..15429.00 rows=1000000 width=12) (actual time=0.010..40.307 rows=1000000.00 loops=1)
                    Buffers: shared hit=5429
              ->  Hash  (cost=831.01..831.01 rows=40001 width=9) (actual time=6.181..6.182 rows=40001.00 loops=1)
                    Buckets: 65536  Batches: 1  Memory Usage: 2227kB
                    Buffers: shared hit=431
                    ->  Seq Scan on stations b  (cost=0.00..831.01 rows=40001 width=9) (actual time=0.013..2.531 rows=40001.00 loops=1)
                          Buffers: shared hit=431
        ->  Hash  (cost=831.01..831.01 rows=40001 width=9) (actual time=6.081..6.082 rows=40001.00 loops=1)
              Buckets: 65536  Batches: 1  Memory Usage: 2227kB
              Buffers: shared hit=431
              ->  Seq Scan on stations e  (cost=0.00..831.01 rows=40001 width=9) (actual time=0.009..2.539 rows=40001.00 loops=1)
                    Buffers: shared hit=431
  ->  Hash  (cost=831.01..831.01 rows=40001 width=9) (actual time=6.839..6.840 rows=40001.00 loops=1)
        Buckets: 65536  Batches: 1  Memory Usage: 2227kB
        Buffers: shared hit=431
        ->  Seq Scan on stations i  (cost=0.00..831.01 rows=40001 width=9) (actual time=0.008..2.281 rows=40001.00 loops=1)
              Buffers: shared hit=431
Planning:
  Buffers: shared hit=18
Planning Time: 0.266 ms
Execution Time: 416.061 ms

EXPLAIN ANALYZE
SELECT B.name, E.name, I.name
FROM hauls_fixed H
JOIN stations B ON B.id=H.border_station_id
JOIN stations E ON E.id=H.export_station_id
JOIN stations I ON I.id=H.import_station_id;

Hash Join  (cost=3993.07..28238.90 rows=1000000 width=15) (actual time=18.645..459.774 rows=1000000.00 loops=1)
  Hash Cond: (h.import_station_id = i.id)
  Buffers: shared hit=7663
  ->  Hash Join  (cost=2662.05..24282.60 rows=1000000 width=14) (actual time=12.302..324.342 rows=1000000.00 loops=1)
        Hash Cond: (h.export_station_id = e.id)
        Buffers: shared hit=7232
        ->  Hash Join  (cost=1331.02..20326.30 rows=1000000 width=13) (actual time=6.391..196.050 rows=1000000.00 loops=1)
              Hash Cond: (h.border_station_id = b.id)
              Buffers: shared hit=6801
              ->  Seq Scan on hauls_fixed h  (cost=0.00..16370.00 rows=1000000 width=12) (actual time=0.009..39.691 rows=1000000.00 loops=1)
                    Buffers: shared hit=6370
              ->  Hash  (cost=831.01..831.01 rows=40001 width=9) (actual time=6.336..6.337 rows=40001.00 loops=1)
                    Buckets: 65536  Batches: 1  Memory Usage: 2227kB
                    Buffers: shared hit=431
                    ->  Seq Scan on stations b  (cost=0.00..831.01 rows=40001 width=9) (actual time=0.007..2.269 rows=40001.00 loops=1)
                          Buffers: shared hit=431
        ->  Hash  (cost=831.01..831.01 rows=40001 width=9) (actual time=5.863..5.863 rows=40001.00 loops=1)
              Buckets: 65536  Batches: 1  Memory Usage: 2227kB
              Buffers: shared hit=431
              ->  Seq Scan on stations e  (cost=0.00..831.01 rows=40001 width=9) (actual time=0.012..2.315 rows=40001.00 loops=1)
                    Buffers: shared hit=431
  ->  Hash  (cost=831.01..831.01 rows=40001 width=9) (actual time=6.298..6.299 rows=40001.00 loops=1)
        Buckets: 65536  Batches: 1  Memory Usage: 2227kB
        Buffers: shared hit=431
        ->  Seq Scan on stations i  (cost=0.00..831.01 rows=40001 width=9) (actual time=0.015..2.631 rows=40001.00 loops=1)
              Buffers: shared hit=431
Planning:
  Buffers: shared hit=18
Planning Time: 0.261 ms
Execution Time: 488.732 ms

Пример приближен к реальности, так как действительно 90% грузоперевозок выполняются внутри РФ. Если же посмотреть на форматы данных АСОУП-3 или ЭТРАН, то там 99% полей nullable (minOccurs=“0” в xsd) и по факту, например в ж/д накладной заполняются в среднем 5% полей. Что, в принципе, логично. Так как ссылки на договора и акты охраны указывается для 0.01% грузоотправок, ссылки на договора и акты сопровождения - 0.1%, есть множество полей, заполняемых только для некоторых видов грузов, локомотивов или видов вагонов и т.п. Привожу в качестве примера, так как xsd обмена с АСОУП-3 и ЭТРАН общедоступны.

Откуда пошли ERP-системы - вопрос интересный.

Потому что это самый простой вариант реальной и достаточно сложной структуры БД, доступ к которой есть у любого желающего. Вы можете предложить иной вариант доступной Вам и мне реальной enterprise БД?

Сколько бы было у вас OUTER JOIN если бы не NULL в FK?

Столько же. Ведь выборка из таблиц операций идет почти всегда от справочников по соответствующим индексам. Что логично, так как в обратном случае мог работать разве что индекс по датам.

Да, если идет агрегация данных, например “справочник с количеством ссылающихся операций” - тогда да, LEFT JOIN нужен. Но так ли много подобных запросов?

Почти в каждом отчете.

Найти искусственный пример,

Зачем искать? Так сложно самому написать скрипт примера?

Я спорю именно с “может приводить к увеличению таблицы в разы”

Какой смысл спорить, если я уже предложил решение этого спора? Называйте ERP и там увидим. Хоть SAP, хоть OEBS, хоть Dynamics.

А минимизация ошибок в запросах лично для меня в приоритете.

Из этого лишь следует, что у Вас крошечные объёмы данных. Иначе бы производительность была бы существенно важнее.

Про JOIN ответил выше

Так себе ответ. OUTER JOIN применяются не редко. Просто потому, в таблице операций может не быть ссылок на все элементы справочника, который в запросе фильтруется по каким-то полям. А отсутствие записей - это тоже результат.

Для примера, в одном из моих текущих проектов по логистике вообще нет nullable PK. При этом там INNER встречается 38 тысяч раз, а OUTER - 18 тысяч раз. То есть, каждый третий JOIN - OUTER.

как часто результаты агрегатных функций попадают в условия?

Да повсеместно. Хоть SUM по нулевому количеству записей или LEAD/LAG на первой или последней записи в PARTITION.

Я не думаю, что “таблица из восьми полей, все могут быть NULL” - очень частая история.

На основании чего такие мысли? Предлагаю взять любую ERP на Ваш выбор и я в ней укажу сходу десятки таких таблиц.

Но есть и выигрыш в производительности от фиксированного размера кортежа - не стоит про это забывать.

Впервые такое слышу. Вот от уменьшения размера кортежа, благодаря которому на одной странице умещается больше кортежей - выигрыш в производительности показать могу легко. Только до компа добраться надо. А сможете ли Вы показать тут повторяемый пример, доказывающий Ваше утверждение?

Не применимы, так как значением NULL=a и NULL<>a будет не false или true, а NULL

Кроме явных случаев с OUTER JOIN, NULL могут возвращать функции. В том числе и агрегатные.

Что касается размеров кортежа, то в PostgreSQL NULL занимает в кортеже один бит, integer - 32 бита, 0::decimal - 3+2 байта = 40 бит. То есть, если в кортеже из восьми integer полей семь NULL, то его общая длина будет 23+1+4=28 байт. А если все восемь ноль - то 23+1+4*8=56 байт. В случае пары десятков decimal или varchar - можете сами посчитать, учитывая, что NULL-mask требует по байту на каждые восемь nullable полей.

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

Упущен важный момент. Немало людей, которым делать одно и то же, которое они уже умеют делать субъективно в совершенстве, просто надоедает и не хочется. Когнитивная нагрузка им просто необходима. Если таким людям не давать периодически задачи, которые требуют усиленной когнитивной нагрузки, они или выгорят, или уйдут.

Потому что это мыши неприспособленные к иерархическому взаимодействию, как люди. Выше я приводил пример с собаками и волками.

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

Надо объяснять, почему ни фильм, ни современную компьютерную игру без иерархии управления создать невозможно?

И как из этого следует, что волки без стаи не вымерли бы? А то, что размер дичи, на которую охотятся волки и тигры одинаков, тогда как их размеры очень разные не смущает? А уж геноцид волков и геноцид тигров даже сравнивать странно. Что-то про массовые истребления тигров с вертолётов я не слышал.

  • Я бы начинал с того, что not manager_id=id тоже не инвертирование условия при наличии NULL. Хотя ошибку чаще допускают, ставя NOT перед скобками с более сложным логическим выражением.

Вы для начала представьте себе жизнь без иерархической организации, когда нет ни пожарных, ни скорой помощи, ни полиции. А потом уже сравнивайте с мышами )))

Для простоты понимания, волки без стаи уже давно бы вымерли.

А иерархическая организация с вожаками есть у всех приматов, включая человека.

Обвинять во лжи, не предъявляя доказательств - это называется клевета. А примера кода, который можно запустить и сравнить с вышеописанным решением хотя бы на тысяче сообщений, порождающих 100 тыс. записей, я что-то не увидел.

При этом проверить, что производительность Sink (даже с моей доработкой и оптимизацией) отличается от вышеприведенного решения почти на два порядка, совсем не сложно.

возвращаясь к человечеству, которое вот грызёт друг дружку как те мышки в опыте Кэлхуна. Я утверждаю, что если начать экспансию в космос, то это переведёт вектор агрессии с друг друга на среду.

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

при увеличении количества кошек - число конфликтов бы росло

Не факт. И среди кошек мог появиться лидер, так же как пёс, взявший на себя роль вожака, обеспечивающего безопасность в обмен на запрет любой агрессии как друг к другу, так и к себе.

Аналогично и у людей, и у собак, и у волков и т.п. Агрессия между членами стаи пресекается вожаком, который так же отвечает за безопасность и выживание стаи. А вот когда бардак и анархия, а не иерархическая организация - вот тогда и начинается агрессия.

1
23 ...

Information

Rating
2,353-rd
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity