суббота, 30 марта 2024 г.

SQL Функции для работы со строками

SELECT LENGTH('Я учу SQL');
SELECT INITCAP ('петров александр александрович');
SELECT LOWER('ПроБЛЕмы сО СтрокАМИ');
SELECT UPPER('ПроБЛЕмы сО СтрокАМИ');
SELECT LTRIM('г. Санкт-Петербург', 'г.');
SELECT REPLACE('Санкт/Петербург', '/', '-');
SELECT CONCAT('Петров', ' ', 'Александр', ' ', 'Александрович');
SELECT LENGTH(CONCAT(billing_address, ' ', billing_city, ' ', billing_country))
FROM invoice
LIMIT 10;

SQL Подзапросы и общие табличные выражения

 

SELECT *
FROM (SELECT m.rating,
             AVG(m.rental_rate) AS average_rental
        FROM movie AS m
      GROUP BY m.rating
      ORDER BY average_rental DESC
      LIMIT 5) AS best_rating;
Подзапросу во FROM нужно всегда назначать псевдоним, иначе произойдёт ошибка. Теперь можно провести вычисления по подзапросу во FROM:
SELECT AVG(best_rating.average_rental)
FROM (SELECT m.rating,
             AVG(m.rental_rate) AS average_rental
        FROM movie AS m
      GROUP BY m.rating
      ORDER BY average_rental DESC
      LIMIT 5) AS best_rating;


SELECT *
FROM client
WHERE customer_id IN (SELECT customer_id
                      FROM invoice
                      GROUP BY customer_id
                      ORDER BY SUM(total) DESC
                      LIMIT 10);
SELECT AVG(rental_rate)
FROM movie AS m
WHERE film_id IN (SELECT m.film_id
                  FROM movie AS m LEFT JOIN film_actor AS fa ON m.film_id = fa.film_id
                  GROUP BY m.film_id
                  HAVING COUNT(actor_id) > 12)
 AND length <= (SELECT AVG(length) 
                FROM movie AS m);
C таблицами, сформированными с помощью подзапросов, можно работать точно так же, как и с таблицами из базы данных. Часто таблицы объединяют уже после того, как получили срез и сгруппировали данные. Причина проста: объединение больших таблиц занимает много времени. Таблицу лучше предварительно обработать, а уже потом объединять данные. Использовать подзапросы — хорошее решение, но зачастую у них очень громоздкий вид. Что будет, если в одном запросе указать несколько подзапросов: пять, шесть и более? Решить проблему могут общие табличные выражения, или CTE (от англ. common table expressions).
WITH
-- назначение псевдонимов и формирование подзапросов
псевдоним_1 AS (подзапрос_1),
псевдоним_2 AS (подзапрос_2),
псевдоним_3 AS (подзапрос_3),
....
псевдоним_n AS (подзапрос_n)

SELECT -- основной запрос
-- внутри основного запроса работают с псевдонимами, которые назначили в WITH
FROM псевдоним_1 INNER JOIN псевдоним_2 ... 
...
...
К общим табличным выражениям нельзя обращаться после оператора WHERE
WITH 
-- первый подзапрос с псевдонимом i
i AS (SELECT billing_country AS country,
             COUNT(total) AS total_invoice
      FROM invoice
      GROUP BY billing_country
      ORDER BY total_invoice DESC
      LIMIT 5), -- подзапросы разделяют запятыми
-- второй подзапрос с псевдонимом c
 c AS (SELECT country AS country,
              COUNT(customer_id) AS total_clients
              FROM client
              GROUP BY country)

-- основной запрос, в котором указаны псевдонимы для подзапросов
SELECT i.country,
       i.total_invoice,
       c.total_clients
FROM i LEFT OUTER JOIN c ON i.country=c.country
ORDER BY i.total_invoice DESC;
WITH
top40 AS (SELECT *       
         FROM movie
         WHERE rental_rate>2
         ORDER BY length DESC
         LIMIT 40)

-- general query
SELECT top40.rating,
       MIN(length) AS min_length,
       MAX(length) AS max_length,
       AVG(length) AS avg_length,
       MIN(rental_rate) AS min_rental_rate,
       MAX(rental_rate) AS max_rental_rate,
       AVG(rental_rate) AS avg_rental_rate
FROM top40
GROUP BY top40.rating
ORDER BY avg_length ASC;
WITH
const_m AS (SELECT *
            FROM generate_series(1, 12) month),
t AS (SELECT 
            EXTRACT(YEAR FROM CAST(invoice_date AS timestamp)) AS year,
            EXTRACT(MONTH FROM CAST(invoice_date AS timestamp)) AS month,
            COUNT(*)
        FROM invoice
        GROUP BY year, month),
t11 AS (SELECT month, count as year_2011 FROM t WHERE year=2011),
t12 AS (SELECT month, count as year_2012 FROM t WHERE year=2012),
t13 AS (SELECT month, count as year_2013 FROM t WHERE year=2013)

SELECT const_m.month as invoice_month, 
       t11.year_2011, t12.year_2012, t13.year_2013
FROM const_m
LEFT JOIN t11 ON t11.month=const_m.month
LEFT JOIN t12 ON t12.month=const_m.month
LEFT JOIN t13 ON t13.month=const_m.month
ORDER BY invoice_month;

пятница, 29 марта 2024 г.

SQL UNION and UNION ALL

SELECT i.billing_country,
       COUNT(i.total) AS total_purchases
FROM invoice AS i
WHERE i.billing_country IN ('USA',
                            'Germany',
                            'Brazil')
AND EXTRACT(YEAR FROM cast(invoice_date AS date)) = 2009
GROUP BY i.billing_country
UNION ALL
SELECT i.billing_country,
       COUNT(i.total) AS total_purchases
FROM invoice AS i
WHERE i.billing_country IN ('USA',
                            'Germany',
                            'Brazil')
AND EXTRACT(YEAR FROM cast(invoice_date AS date)) = 2013
GROUP BY i.billing_country;

  • Оператор UNION устроен так, что абсолютные дубликаты в итоговую таблицу не входят.
  • UNION ALL не удаляет полные дубликаты

SQL порядок выполнения операторов

Операторы действуют в таком порядке: 
  1. Сначала нужно определить, откуда брать данные, поэтому первым идёт оператор FROM. На этом же этапе объединяются таблицы операторами JOIN и назначаются для них псевдонимы. Важно учесть, что присоединение предшествует фильтрации и группировке. Это означает, что большие таблицы будут объединяться очень долго. 
  2. Данные выбраны, и наступает очередь оператора WHERE. Остаются только те данные, которые соответствуют условиям. 
  3. После срезов данные группируются оператором GROUP BY и подсчитываются агрегирующими функциями. Обратите внимание, что WHERE предшествует GROUP BY, и это не позволяет сделать срез по группам. В момент получения среза данные ещё не сгруппировались. 
  4. Теперь наступает очередь HAVING — отбираются уже сгруппированные данные. 
  5. Только на этом этапе происходит выбор данных с помощью оператора SELECT, а полям в итоговой таблице присваиваются псевдонимы. По этой причине псевдонимы нельзя использовать после WHERE и HAVING — они ещё не назначены. В некоторых СУБД псевдонимы нельзя использовать и после GROUP BY. В PostgreSQL есть расширение, которое устраняет эту проблему. 
  6. После SELECT срабатывает ключевое слово DISTINCT, которое отбирает уникальные значения. Нужные данные отобраны, и происходит сортировка. 
  7. Оператор ORDER BY действует предпоследним. 
  8. Замыкающим будет оператор LIMIT.

SQL взаимоотношения между таблицами

entity-relationship diagram 
PK - Primary key
FK - Foreign key

Переименование полей и таблиц. Псевдонимы

SELECT EXTRACT(YEAR FROM CAST(i.invoice_date AS DATE)) AS year_of_purchase,
       MIN(i.total) AS min_cost,
         MAX(i.total) AS max_cost,
         SUM(i.total) AS total_revenue,
         COUNT(i.total) AS total_purchases,
         ROUND(SUM(i.total)/COUNT(DISTINCT(i.customer_id))) AS average_receipt
FROM invoice AS i
WHERE billing_country IN ('USA', 'United Kingdom', 'Germany')
GROUP BY year_of_purchase
ORDER BY year_of_purchase DESC;
К псевдонимам нельзя обратиться в WHERE или HAVING, потому что в SQL операторы выполняются не в том порядке, в котором их указывают в запросе. Сначала данные отбираются по условию, а только потом полям назначаются псевдонимы. Во многих СУБД нельзя обращаться к псевдонимам и в GROUP BY, но в PostgreSQL можно.
SELECT billing_country,
       count(invoice_id) AS total_purchases,
       SUM(total) AS total_revenue,
       ROUND(AVG(total), 2) AS average_revenue
FROM invoice
GROUP BY billing_country
ORDER BY average_revenue DESC
LIMIT 10;
SELECT rating AS rating_of_epic,
       release_year AS year_of_epic,
       AVG(rental_duration) AS average_rental
FROM movie
WHERE description LIKE '%Epic%'
GROUP BY rating_of_epic, release_year;
SELECT CASE
           WHEN movie.rating = 'G' THEN 'без ограничений'
           ELSE 'с ограничениями'
       END AS new_rating,
       SUM(rental_rate)
FROM movie
GROUP BY new_rating;

Операторы JOIN

  • INNER JOIN
  • LEFT OUTER JOIN
  • RIGHT OUTER JOIN
  • FULL OUTER JOIN
SELECT t.name,
	   SUM(i.quantity)
FROM track AS t
INNER JOIN invoice_line AS i ON i.track_id=t.track_id
GROUP BY t.name
LIMIT 20;
SELECT t.name,
       SUM(i.quantity),
       p.playlist_id
FROM track AS t
INNER JOIN invoice_line AS i ON t.track_id=i.track_id
INNER JOIN playlist_track AS p ON t.track_id=p.track_id
GROUP BY t.name, p.playlist_id
LIMIT 20;
SELECT t.name,
       SUM(i.quantity),
       pt.playlist_id,
       pl.name
FROM track AS t
INNER JOIN invoice_line AS i ON t.track_id=i.track_id
INNER JOIN playlist_track AS pt ON t.track_id=pt.track_id
INNER JOIN playlist AS pl ON pt.playlist_id=pl.playlist_id
GROUP BY t.name, pt.playlist_id, pl.name
LIMIT 20;
SELECT p.name AS playlist_name,
       SUM(i.unit_price) AS total_revenue
FROM track AS t
INNER JOIN invoice_line AS i ON t.track_id=i.track_id
INNER JOIN playlist_track AS pl ON t.track_id=pl.track_id
INNER JOIN playlist AS p ON pl.playlist_id = p.playlist_id
GROUP BY playlist_name
ORDER BY total_revenue DESC;
SELECT genre.name,
       SUM(il.quantity) AS orders
FROM genre
INNER JOIN track ON genre.genre_id=track.genre_id
INNER JOIN invoice_line AS il ON track.track_id = il.track_id
--INNER JOIN invoice AS i ON il.invoice_id=i.invoice_id
GROUP BY genre.name
ORDER BY orders DESC;
SELECT DISTINCT category.name
FROM film_category
INNER JOIN film_actor ON film_category.film_id=film_actor.film_id
INNER JOIN actor ON actor.actor_id=film_actor.actor_id
INNER JOIN category ON film_category.category_id=category.category_id
WHERE actor.first_name = 'Uma' AND actor.last_name = 'Wood';
SELECT art.name,
       alb.title
FROM artist AS art
LEFT OUTER JOIN album AS alb ON art.artist_id = alb.artist_id
LIMIT 10;
Таблицы artist и album объединены по ключу artist_id, и благодаря оператору LEFT OUTER JOIN все значения из поля name таблицы artist попадут в итоговую таблицу. Если для какого-то исполнителя не найдётся альбома, на этом месте в поле title появится значение NULL.
SELECT art.name,
       alb.title
FROM album AS alb
RIGHT OUTER JOIN artist AS art ON art.artist_id = alb.artist_id
LIMIT 10;
SELECT a.actor_id,
       a.first_name,
       a.last_name,
       c.first_name,
       c.last_name
FROM actor AS a
FULL OUTER JOIN client AS c ON a.last_name = c.last_name
LIMIT 10;
Крупные таблицы при использовании FULL OUTER JOIN могут упереться в оперативную память.

понедельник, 18 марта 2024 г.

Lifetime

Время от момента регистрации пользователя продукта до момента наступления какого-то важного с точки зрения бизнеса события.
Lifetime = T - T0




# загружаем данные
data = pd.read_csv('flowers.csv')

# приводим столбцы к типу datetime (дата и время)
for c in ['install_dt', 'event_dt']: 
    data[c] = pd.to_datetime(data[c])

data['lifetime'] = data['event_dt'] - data['install_dt']

data['lifetime_sec'] = (data['event_dt'] - data['install_dt']).dt.total_seconds()
data['lifetime_days'] = (data['event_dt'] - data['install_dt']).dt.days
# недели
data['lifetime_weeks'] = (data['event_dt'] - data['install_dt']) / np.timedelta64(1, 'W')
# месяцы
data['lifetime_months'] = (data['event_dt'] - data['install_dt']) / np.timedelta64(1, 'M')
# года
data['lifetime_years'] = (data['event_dt'] - data['install_dt']) / np.timedelta64(1, 'Y')



Используется для:
  • сегментации пользователей "по возрасту" (масштаб дни... месяцы...)
  • first user experience (масштаб секунды... минуты...)
  • когортный анализ (первые и повторные покупки)
выбор шкалы времени зависит от специфики продукта, например для интернет магазинов это м.б. месяцы; для онлайн образования масштаб шкалы примерно соответствует длине спринта, около 1-2 недель; для мобильных игр и приложений это могут быть дни и недели;

Классы задач (примеры):
  • доля старых пользователей во всех пользователях (соотношение старый и новый, при этом старый/новый могут определяться по разному)
  • различия в монетизации для старых/новых пользователей, например средний чек
  • отвал пользователей в приложении или онлайн игре, можно локализовать время максимального оттока пользователей
  • изучение пользователей возобновляющих подписку к каждом периоде с момента регистрации




SQL агрегирующие функции, GROUP BY, ORDER BY и HAVING

Стандартные операторы + - * /

SELECT invoice_line_id,
       invoice_id,
       invoice_line_id + invoice_id
FROM invoice_line
LIMIT 5;

Модуль числа

SELECT number,
       ABS(number)
FROM table_1;

Округление до целого в меньшую сторону

SELECT number,
       FLOOR(number)
FROM table_1;

Округление до целого в большую сторону

SELECT number,
       CEILING(number)
FROM table_1;

Округление до ближайшего целого

SELECT number,
       ROUND(number)
FROM table_1;

Округление до заданного количества знаков

SELECT ROUND(21.5595743, 2);

Возведение в степень

SELECT number,
       POWER(number, 2)
FROM table_1;

Квадратный корень

SELECT number,
       SQRT(ABS(number))
FROM table_1;

Основные агрегирующие функции в SQL:

  • SUM(поле) возвращает сумму значений в поле;
  • AVG(поле) находит среднее арифметическое для значений в поле;
  • MIN(поле) возвращает минимальное значение в поле;
  • МАХ(поле) возвращает максимальное значение в поле;
  • COUNT(*) выводит количество записей в таблице, а COUNT(поле) — количество записей в поле.
SELECT SUM(total),
       MIN(total),
       MAX(total)
FROM invoice;
SELECT ROUND(AVG(total), 2)
FROM invoice;
-- так считают записи в таблице
SELECT COUNT(*)
FROM invoice;
-- так считают записи в поле
SELECT COUNT(billing_postal_code)
FROM invoice;
 -- уникальные значения без дубликатов
SELECT DISTINCT billing_country 
FROM invoice
LIMIT 5;
 -- а так уникальные сочетания значений полей
SELECT DISTINCT customer_id,
                billing_country
FROM invoice
LIMIT 10;
 -- DISTINCT можно сочетать с агрегирующими функциями
SELECT COUNT(DISTINCT billing_country)
FROM invoice;
Примеры использования агрегирующих функций:
SELECT customer_id,
       CAST(invoice_date AS date),
       total
FROM invoice
WHERE EXTRACT(MONTH FROM CAST(invoice_date AS timestamp)) = 9
      AND customer_id IN (11, 13, 44, 36, 48, 52, 54, 56);
SELECT MIN(total),
       MAX(total),
       FLOOR(AVG(total)),
       COUNT(customer_id),
       SUM(total)
FROM invoice
WHERE EXTRACT(MONTH FROM CAST(invoice_date AS timestamp)) = 9
  AND customer_id in (11, 13, 44, 36, 48, 52, 54, 56);
SELECT COUNT(*)
FROM client
WHERE fax IS NULL;
-- средняя стоимость заказов оформленная в понедельник
SELECT AVG(total)
FROM invoice
WHERE EXTRACT(DOW FROM CAST(invoice_date AS timestamp))=1;

Группировка данных

-- нужно указать поля и необходимые вычисления
SELECT billing_city,
       ROUND(AVG(total))
FROM invoice
GROUP BY billing_city -- здесь указывают поле, по которому группируют данные
LIMIT 5;



 -- выгрузит все уникальные значения в поле. Похоже на работу DISTINCT
SELECT billing_city
FROM invoice
GROUP BY billing_city;
SELECT SUM(total)
FROM invoice
WHERE billing_country = 'USA';
SELECT billing_city,
       SUM(total),
       COUNT(*),
       AVG(total)
FROM invoice
WHERE billing_country = 'USA'
GROUP BY billing_city;
SELECT DATE_TRUNC('week', CAST(invoice_date AS timestamp)),
       SUM(total),
	   COUNT(DISTINCT customer_id),
	   SUM(total)/COUNT(DISTINCT customer_id)
FROM invoice
WHERE billing_country = 'USA'
GROUP BY DATE_TRUNC('week', CAST(invoice_date AS timestamp));
SELECT support_rep_id,
       COUNT(customer_id)
FROM client
WHERE email LIKE '%yahoo%' OR email LIKE '%gmail%'
GROUP BY support_rep_id;
SELECT CASE
            WHEN total<1 THEN 'low cost'
            ELSE 'high cost'
       END,
       SUM(total)
FROM invoice
WHERE billing_postal_code IS NOT NULL
GROUP BY CASE
            WHEN total<1 THEN 'low cost'
            ELSE 'high cost'
         END;
 -- сортировка
SELECT billing_city,
       AVG(total)
FROM invoice
WHERE billing_country = 'USA'
GROUP BY billing_city
ORDER BY AVG(total) DESC -- или ASC
LIMIT 5;
SELECT customer_id,
       COUNT(customer_id)
FROM invoice
WHERE CAST(invoice_date as date) BETWEEN '2011-05-25' AND '2011-09-25'
      AND billing_country = 'USA'
GROUP BY customer_id
ORDER BY COUNT(customer_id) DESC, customer_id
LIMIT 5;
SELECT EXTRACT(YEAR FROM CAST(invoice_date as date)),
       MIN (total),
       MAX (total),
       SUM (total),
       COUNT (invoice_id),
       ROUND(SUM(total)/COUNT(DISTINCT customer_id))
FROM invoice
WHERE billing_country IN ('USA', 'United Kingdom', 'Germany')
GROUP BY EXTRACT(YEAR FROM CAST(invoice_date as date))
ORDER BY EXTRACT(YEAR FROM CAST(invoice_date as date)) DESC;
Группировка по нескольким полям сразу
SELECT CAST(invoice_date AS date),
       billing_country,
       COUNT(total)
FROM invoice
GROUP BY CAST(invoice_date AS date),
         billing_country 
ORDER BY CAST(invoice_date AS date) DESC
LIMIT 10;
При сортировке важен порядок полей, которые указывают после оператора ORDER BY

SELECT billing_country,
       customer_id,
       total
FROM invoice
WHERE billing_country = 'India' or billing_country = 'Portugal'
ORDER BY billing_country,  -- и наконец по стране
         customer_id,  -- вторым шагом по customer_id
         total DESC; -- сначала сортируем по total

Оператор HAVING

Оператор WHERE работает только с изначальной таблицей, но не с результатом агрегирующих функций. Если нужно получить срез данных после группировки, используют оператор HAVING. HAVING похож на оператор WHERE, но с отличием: HAVING всегда идёт после GROUP BY. HAVING можно сочетать с агрегирующими функциями
SELECT customer_id,
       SUM(total)
FROM invoice
GROUP BY customer_id
HAVING SUM(total) > 41
ORDER BY SUM(total) DESC;
SELECT customer_id,
       SUM(total)
FROM invoice
GROUP BY customer_id
HAVING SUM(total) > 41 AND AVG(total) > 7
ORDER BY SUM(total) DESC;
SELECT billing_country,
       COUNT(*)
FROM invoice
WHERE billing_postal_code IS NULL
      AND billing_address NOT LIKE '%Street%'
           AND billing_address NOT LIKE '%Way%'
           AND billing_address NOT LIKE '%Road%'
           AND billing_address NOT LIKE '%Drive%'
GROUP BY billing_country
HAVING COUNT(*)>10;

воскресенье, 17 марта 2024 г.

Что необходимо для выделения когорты

  • изначально объединяющее событие (первое посещение сайта, регистрация или скачивание мобильного приложения. Так в анализ попадает только определённая категория людей — впервые посетившие сайт, зарегистрированные в программе лояльности, пользователи приложения.)
  • время
  • дополнительный признак (может отсутствовать)

Обычно BI системы ведут два основных журнала:
  • журнал посещений
    • uid
    • дата начала сессии
    • длительность (или дата окончания)
    • другие параметры (устройство, география, источник и т.п.)
  • журнал покупок
    • uid
    • дата покупки
    • сумма
    • м.б. состав покупки

Ниже на основании журналов подготовим профили пользователей для когортного анализа. 

import pandas as pd

sessions = pd.read_csv('sessions.csv')  #https://drive.google.com/file/d/1mM4m4W_jWH8KqicHjJguw70ViO5_ldgK/view?usp=share_link
sessions['session_start'] = pd.to_datetime(sessions['session_start'])

orders = pd.read_csv('book_orders.csv')  #https://drive.google.com/file/d/1BRxsxvtYXrADqD6fXd_YqXgo4xiXeCRb/view?usp=share_link
orders['event_dt'] = pd.to_datetime(orders['event_dt'])

# добавили второй аргумент
def get_profiles(sessions, orders=None):

    profiles = (
        sessions.sort_values(by=['user_id', 'session_start'])
        .groupby('user_id')
        .agg(
            {
                'session_start': 'first',
                'channel': 'first',
                'device': 'first',
                'region': 'first',
                # другие параметры
            }
        )
        .rename(columns={'session_start': 'first_ts'})
        .reset_index()
    )

    profiles['dt'] = profiles['first_ts'].dt.date
    profiles['month'] = profiles['first_ts'].to_numpy().astype('datetime64[M]')
    
    # проверьте, есть ли ID пользователей из profiles в orders
    if orders is not None:
        payers = set(orders.user_id.unique())
        profiles['payer'] = profiles['user_id'].map(lambda x: True if x in payers else False)

    return profiles

# строим профили по двум наборам данных
profiles = get_profiles(sessions, orders)

print(profiles)

Простая продуктовая воронка

В данных должны содержаться три обязательных поля:
  • дата и время - event_time
  • идентификатор пользователя uid
  • имя события event_name

events_count = events.groupby('event_name').agg({'uid': 'count'})
в первом приближении так, то пользователи могут вызывать одно и тоже событие по нескольку раз, например, открывать окно чата с сервисом, поэтому правильнее будет использовать количество уникальных пользователей на каждом этапе воронки.
users_count = events.groupby('event_name').agg({'uid': 'nunique'}).sort_values(by='uid', ascending=False)

пятница, 15 марта 2024 г.

SQL basics (based on PostgreSQL)

Простейшие

  • SELECT 'на экране отображается число', 5;
  • -- Так выглядит однострочный комментарий
  • многострочный комментарий ниже
/*В век высоких технологий
Без БД не обойтись,
SQL — язык запросов
Легче делает нам жизнь!*/

  • SELECT * FROM genre;
  • SELECT * FROM media_type;
  • SELECT * FROM playlist;





Как ограничивать выборку

SELECT поле_1,
       поле_2,
       поле_3
       ...
FROM таблица
LIMIT 10 OFFSET 5;

SELECT email,
       first_name,
       last_name
FROM client;

SELECT invoice_id,
       customer_id,
       invoice_date,
       billing_address
FROM invoice
LIMIT 5
OFFSET 0;

SELECT name,
       unit_price
FROM track
LIMIT 20
OFFSET 0;

SELECT *
FROM track
LIMIT 3
OFFSET 7; -- выводим строки с 8 по 10 включительно

Типы данных

  • integer от -2147483648 до +2147483647
  • real
  • character обозначают псевдонимом char(n), если не указывать n, то по умолчанию он 1. n- количество символов в хранимой строке (меньше можно, больше нельзя)
  • character varying, псевдоним varchar(n) - тексты нефиксированной длины. Значение n управляет лимитом строки, но его можно не указывать.
  • text - содержит строку любой длины. Такого типа в стандарте SQL нет, но в некоторых СУБД, помимо PostgreSQL, его тоже используют.
  • timestamp - содержит дату и время. Чаще всего дату и время указывают в формате ISO: 'YYYY-MM-DD'. Например, 30 июня 2010 года записывают так: '2010-06-30' (timestamp with time zone или timestamptz, timestamp without time zone в Postgres по умолчанию)
  • В тип date входит только дата. Дату можно задавать в любом формате, например, October 19, 2004, 2004-Oct-19 или даже 20041019.
  • Тип time хранит только время и принимает значения от 00:00:00 до 24:00:00.
  • Тип interval используют для обозначения интервала между датами. Задать нужный интервал можно с помощью условных обозначений или в свободной форме. Например, запись '1 12:59:10' обозначает 1 день 12 часов 59 минут 10 секунд.
  • boolean - Для TRUE список аналогий такой: 'true', 't', 'yes', 'y', 'on', '1'. Вместо FALSE можно написать: 'false', 'f', 'no', 'n', 'off', '0'.
Поменять тип данныйх можно сразу при выгрузке данных, при этом в самой таблице тип не изменится


SELECT CAST(track_id AS varchar),
       CAST(album_id AS varchar)
FROM track;
SELECT CAST(milliseconds AS varchar),
       CAST(bytes AS varchar)
FROM track;
SELECT CAST(total as int)
FROM invoice;
SELECT CAST(birth_date AS date)
FROM staff;

Оператор WHERE. Операторы сравнения

SELECT track_id, -- поля для выгрузки
       album_id 
       ...
FROM track -- таблица, из которой выгружают данные
WHERE track_id = 5; -- условие для среза данных >, <, >=, <=
SELECT *
/* Нужно выгрузить всю информацию о заказах,
поэтому поля можно не указывать*/
FROM invoice
WHERE total >= 1.2;
SELECT *
FROM invoice_line
WHERE unit_price>0.99;
SELECT first_name,
       last_name,
       city
FROM client
WHERE country='Brazil';
SELECT billing_address,
       CAST(invoice_date as date)
FROM invoice
WHERE total>=8;
SELECT invoice_id,
       billing_country,
       CAST(invoice_date as date)
FROM invoice
WHERE CAST(invoice_date as date)>='2009-01-06';

Логические операторы

WHERE city = 'Тула'
  AND age > 43;
WHERE city = 'Тула'
  OR city = 'Пермь';
WHERE NOT city = 'Тула'
  AND NOT city = 'Пермь';

Условия с оператором AND обладают большим приоритетом, чем условия с OR.
WHERE age > 43
  AND city = 'Тула'
  OR city = 'Пермь'
  AND age < 43;
WHERE age > 43
  AND (city = 'Тула'
  OR city = 'Пермь');
SELECT total,
       customer_id
FROM invoice
WHERE billing_city = 'Dublin'
      OR billing_city = 'London'
      OR billing_city = 'Paris';
SELECT total,
       customer_id
FROM invoice
WHERE total >= 5
      AND (customer_id = 40
           OR customer_id = 46);
SELECT total,
       customer_id
FROM invoice
WHERE (billing_city = 'Dublin'
      OR billing_city = 'London'
      OR billing_city = 'Paris')
      AND total >= 5
      AND (customer_id = 40
          OR customer_id = 46);
SELECT last_name,
       phone
FROM client
WHERE country IN ('USA', 'France')
      AND support_rep_id = 3;
SELECT title
FROM movie
WHERE rental_rate<=2
      AND rental_duration>6
      AND rating NOT IN ('PG', 'PG-13');
SELECT billing_address,
       billing_city
FROM invoice
WHERE
      CAST(invoice_date as date) BETWEEN '2009-09-01' AND '2009-09-30'
      AND NOT billing_country IN ('USA', 'Brazil')
      AND total>=2;
SELECT *
FROM invoice
WHERE customer_id BETWEEN 23 AND 48;

Оператор LIKE

Оператор регистрозависимый
SELECT *
FROM playlist_track
WHERE CAST(track_id AS varchar) LIKE '%43%'; 
SELECT name
FROM playlist
WHERE name LIKE '%Classic%';
SELECT billing_address,
       billing_country
FROM invoice
WHERE billing_country IN ('USA', 'India', 'Canada', 'Argentina', 'France');
SELECT billing_address,
       billing_country
FROM invoice
WHERE billing_country IN ('USA',
                          'India',
                          'Canada',
                          'Argentina',
                          'France')
		  AND billing_city NOT IN ('Redmond',
                                   'Lyon',
                                   'Delhi');
SELECT *
FROM invoice
WHERE CAST(invoice_date as date) BETWEEN '2009-03-04' AND '2012-02-09'
      AND total<=5
      AND billing_country NOT IN ('Canada', 'Brazil', 'Finland');
SELECT title
FROM movie
WHERE description LIKE '%Mexico'
      AND (rental_rate<2 code="" or="" rating="">
SELECT name
FROM track
WHERE (milliseconds>300000
       AND composer LIKE '%Bono%'
       AND genre_id IN (7, 8, 9, 10))
      OR
      (bytes>1000000000);

Операторы и функции для работы с датой и временем

  • CURRENT_DATE вернёт текущую дату, 
  • CURRENT_TIME выведет текущее время, 
  • CURRENT_TIMESTAMP вернёт текущие дату и время.
SELECT CURRENT_DATE;
DATE_TRUNC('отрезок времени', поле) 
  • 'microseconds' — микросекунды; 
  • 'milliseconds' — миллисекунды; 
  • 'second' — секунда; 
  • 'minute' — минута; 
  • 'hour' — час; 
  • 'day' — день; 
  • 'week' — неделя; 
  • 'month' — месяц; 
  • 'quarter' — квартал; 
  • 'year' — год; 
  • 'decade' — десятилетие; 
  • 'century' — век.
SELECT DATE_TRUNC('year', birth_date)
FROM staff
LIMIT 5;
Данный запрос вернет дату округленную до первого числа каждого года. 

EXTRACT(отрезок времени FROM поле) 
  • CENTURY — век; 
  • DAY — день; 
  • DOY (от англ. day of the year) — день года, выраженный числом от 1 до 365 или 366, если год високосный; 
  • DOW (от англ. day of the week) — день недели, выраженный числом от 0 до 6, где понедельник — 1, воскресенье — 0. 
  • ISODOW (от англ. day of the week и ISO 8601) — день недели, выраженный числом от 1 до 7, где понедельник — 1, воскресенье — 7. 
  • HOUR — час; 
  • MILLISECOND — миллисекунда; 
  • MINUTE — минута; 
  • MONTH — месяц; 
  • SECOND — секунда; 
  • QUARTER — квартал; 
  • WEEK — неделя в году; 
  • YEAR — год.
SELECT EXTRACT(YEAR FROM birth_date)
FROM staff
LIMIT 5;
Функции DATE_TRUNC и EXTRACT принимают на вход данные тех типов, которые используют для работы с датой и временем. Но есть особенности. Типы date и time функции автоматически переведут в timestamp with time zone. У этой проблемы есть решение — явно изменить тип данных на timestamp. Напомним, что в SQL так по умолчанию обозначается тип timestamp without time zone. С типом данных без часового пояса время не сместится.




SELECT customer_id,
       invoice_date,
       total,
       DATE_TRUNC('month', CAST(invoice_date AS timestamp)),
       EXTRACT(WEEK from CAST(invoice_date AS timestamp))
FROM invoice
WHERE customer_id BETWEEN 20 AND 50;
SELECT customer_id,
       invoice_date,
       total,
       DATE_TRUNC('month', CAST(invoice_date AS timestamp)),
       EXTRACT(WEEK FROM CAST(invoice_date AS timestamp))
FROM invoice
WHERE customer_id BETWEEN 20 AND 50 
      AND EXTRACT(WEEK FROM CAST(invoice_date AS timestamp)) IN (5, 7, 10, 33, 48);
SELECT *
FROM invoice
WHERE EXTRACT(DAY FROM CAST(invoice_date AS timestamp))=1;
SELECT email
FROM staff
WHERE city='Calgary'
      AND EXTRACT(YEAR FROM hire_date)=2002;
SELECT DATE_TRUNC('week', CAST(invoice_date AS timestamp)),
       DATE_TRUNC('week', CAST(invoice_date AS timestamp))+'1 00:00:00',
       EXTRACT(DOW FROM CAST(invoice_date AS timestamp)),
       invoice_date
FROM invoice;
NULL это пропуски данных. С NULL нельзя сравнить какое-либо значение с помощью любых операторов: =, <, >, LIKE. Для работы со специальными значениями используют отдельные операторы — IS NULL и IS NOT NULL.
SELECT email,
         fax
FROM client
WHERE fax IS NULL
LIMIT 5;
SELECT billing_city
FROM invoice
WHERE billing_postal_code IS NULL;
SELECT billing_city
FROM invoice
WHERE billing_postal_code IS NULL
      AND billing_state IS NOT NULL
      AND total >= 15;
SELECT album_id
FROM track
WHERE milliseconds>250000
      AND name LIKE '%Moon%'
      AND composer IS NULL;
SELECT first_name,
       last_name,
       country
FROM client
WHERE company IS NULL
      AND state IS NULL
      AND phone IS NULL
      AND fax IS NULL
Конструкция CASE Конструкция с оператором CASE выводит новое поле с результатом проверки условий. Это поле появляется только в выдаче, а оригинальная таблица остаётся без изменений.
SELECT total,
       CASE
           WHEN total < 5 THEN 'маленький'
           WHEN total >= 5 AND total < 10 THEN 'средний'
           WHEN total >= 10 THEN 'крупный'
       END
FROM invoice
LIMIT 10;



Если значение в проверяемом поле не удовлетворяет ни одному условию, результатом будет NULL. Условия в запросе проверяются по порядку. Если условие выполнено, сразу выводится соответствующий результат, а остальные условия не проверяются.


SELECT total,
       CASE
           WHEN total >= 5 AND total < 10 THEN 'средний'
           WHEN total >= 10 THEN 'крупный'
           ELSE 'маленький'
       END
FROM invoice
LIMIT 10;
SELECT last_name,
       first_name,
       title,
      CASE 
          WHEN title LIKE '%Manager%' AND title NOT LIKE '%IT%' THEN 'отдел продаж'
          WHEN title LIKE '%IT%' THEN 'разработка'
          WHEN title LIKE '%Support%' THEN 'поддержка'
      END
FROM staff;
SELECT title,
       rental_rate,
    CASE
        WHEN rental_rate<1 THEN 'категория 1'
        WHEN rental_rate>=1 AND rental_rate<3 THEN 'категория 2'
        WHEN rental_rate>=3 THEN 'категория 3'
    END
FROM movie;

вторник, 12 марта 2024 г.

Tableau dashboard example


  • В области редактирования дашборда можно менять его внешний вид. Здесь будут размещаться все необходимые визуализации. 
  • В области управления размером настраивается то, как дашборд будет выглядеть на разных типах устройств. Ему можно задать определённое положение на экране и расширение. 
  • В области выбора визуализаций содержится список созданных ранее таблиц, графиков и диаграмм. Их можно перетащить в область редактирования дашборда. 
  • В области выбора дополнительных объектов находятся вспомогательные элементы, которые также можно добавить на дашборд.

Перечень дополнительных объектов: 
  • Horizontal — контейнер, на котором визуализации можно расположить в один горизонтальный ряд. 
  • Vertical — вертикальный контейнер. 
  • Text — текстовые описания и заголовки. 
  • Image — изображения. 
  • Web page — окно браузера, в котором можно открыть веб-страницу. 
  • Blank — пустой контейнер, в который нельзя добавить другие элементы. 
  • Navigation — кнопка для перехода к другой визуализации или другому дашборду. 
  • Download — кнопка для скачивания дашборда в формате PDF. 
  • Extensions — расширения из библиотеки Tableau. Их используют редко, поэтому в этом курсе мы их не рассматриваем. 
  • Ask Data — элемент, позволяющий пользователям дашбордов задавать вопросы к данным с помощью обычных предложений (примерно так, как вы ищете данные в поисковиках). К сожалению, он работает только в платных версиях Tableau.

воскресенье, 10 марта 2024 г.

Tableau story example

 


    • Область управления внешним видом служит для выбора визуализаций в истории, добавления слайдов (story point) и управления их размером.
    • В области навигации по слайдам можно переключаться между слайдами на этапах создания и демонстрации истории.
    • Область показа визуализации служит для демонстрации графика или дашборда, который находится на слайде.

    суббота, 9 марта 2024 г.

    Несколько слов о процессах

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

    Он-боардинг - процесс погружения в коллектив, работу и задачи. Знакомство с командой и получение логинов и паролей. Более сложная часть состоит в том чтобы за отведенное время (например месяц или два) разобраться с процессами и научиться оценивать задачи и сроки. Чтение confluence (корпоративной вики) где вероятно будет рассписано все: отвественные, подходы к codestyle, архитектуре, ревью кода и т.д. Знакомимся со всем этим и получаем первые задачи.

    Ошибки на первых этапах неизбежны, кривые оценки, косяки с соблюдением стандартов. В нормальных процессах время на эти ошибки на первое время закладывается заранее.

    Следующий шаг после (во время онбординга) разобраться по какой методологии идет разработка проекта. Задать вопрос PMM. Скорее всего придется работать не с реальными методологиями, а с их разными интерпретациями. Методология это описанный алгоритм, по которому работает команда над проектом.

    Например существует методология waterflow. По ней строят технически сложные инженерные сооружения типа электростанций. Когда очень точно до мельчайшей детали формулируется что мы хотим получить, составили все чертежи и технические параметры. Потом расписали весь процесс изготовления на микроуровне (как схема сборки мебели икея или конструктора лего). затем мы расписали для каждой задачи конкретного ответсвенного и сроки (с выстроенным календарем и диаграммой Ганта). Как итог мы знаем результат, знаем сроки, знаем исполнителей и все-все-все.

    В чистом виде waterflow в IT невозможен (однако может быть применим для некоторых критических компонент). Часто исполнители не понимают как решать задачу, а менеджеры исполнителей до конца не понимают саму задачу, поэтому планы приходится менять по несколько раз в день.


    Kanban - это инструмент, который позволяет удобно организовать работу с помощью стикеров с описанием задач и доски со статусами для этих задач. Весь процесс ведущий к созданию ценности разбит на мелкие задачи (т.е. декомпозирован), здесь удобно привести пример с ремонтом. Обычно статусы для декомпозированных задач выбираются следующие:

    • бэклог (все что еще не оценили)
    • в плане (все что уже оценили и взяли в спринт)
    • в работе
    • заблокирован (когда есть препятствующая задача, блокер)
    • готово
    У каждой карточки есть оценка по времени. Сам flow (последовательность статусов) задается бизнес процессом и может иметь другой набор статусов, ветвлений и пр.

    Agile - это философия, итеративный подход в разработке продуктов, который фиксируется на непрерывных выпусках и учете отзывов клиентов на каждой итерации. Первый шаг - все не стесняются того что никто в компании ничего не понимает в продукте, это не стыдно и по факту правда. Многие продукты в конечном счете получились не тем чем они задумывались изначально. Чаще всего требования заказчика или менеджера не являются ТЗ.
    1. мы принимаем то что финальный результат не зафиксирован
    2. мы не только разрешаем заказчику вмешиваться, но и делаем этот процесс комфортным
    3. всеми силами пытаемся не превратить проект в хаос
    Scrum - is an agile project management framework that helps teams structure and manage their work through a set of values, principles, and practices.
    Это конкретный рецепт как организовать работу по  Agile и не сойти с ума. Разбивает работу на цели, которые должны быть выполнены в течении ограниченных по времени итераций, называемых спринтами.

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

    Ритуалы скрама.
    1. отделяем необходимые созвоны от мусора
    2. найти регулярные
    3. запланировать их в конкретное время в календаре
    4. обязать всех участников готовиться к созвонам
    Как итог (пример):
    • Каждый понедельник в 11 утра планирование (какие появились новые задачи, оценка, работа с backlog, что не успели сделать за прошлый спринт, выбор из всей этой кучи тех задач, которые хотим сделать за этот и распределение внутри команды)
    • Раз или два в нелелю daily - это короткий созвон, где вы говорите как ваши дела, он нужен для того чтобы поделиться с командой о затыках в решении задач.
    • Регулярный или не регулярный груминг. Это чистка бэклога и вычесывание оттуда лишнего и ненужного
    • В конце спринта ревью и ретро. На котором обсуждается что успели и не успели, почему, что хорошо, что могло быть лучше и благодарим коллеги за хорошую работу (а за не хорошую не благодарим :))
    • далее по кругу...
    Сложно бывает эти регламенты соблюдать.

    Что такое покер планирование? (идет в комплекте со скрамом, один из ритуалов)
    Как быстро и безошибочно дать им сроки и оценку сложности. (спойлер никак, однако можно сделать следующее:...) 

    Для сотрудников с разным уровнем и опытом будут разные оценки сроков и сложности.
    Если все задачи оценит "сильный" сотрудник, то "слабый" не успеет их сделать.
    Если все задачи оценит "слабый", то "сильный" будет простаивать.
    Можно разделить задачи между сотрудниками и пусть каждый оценит свою, но...
    Что если "сильный" сотрудник захочет поиграть в WoW и напишет себе много часов (даст завышенную оценку задачам), или он оценит их "честно" но заболеет и "слабый" сотрудник взявший его задачи точно не выдержит сроки.

    Менеджеру нужна точная, честная и универсальная оценка (так быть не может, но покер планирование помогает построить теоретико игровой процесс так чтобы приблизиться к идеалу). Как это происходит: 
    1. команда собирается, обсуждаем задачу #N
    2. каждому члену команды дается полторы минуты и листочек на подумать и каждый в закрытую пишет сколько это задача займет часов
    3. когда время вышло все вскрывают свои карточки
    4. берутся вылеты, сначала обсуждают самую дешевую оценку, как возможно реализовать это за такой короткий срок; потом берется тот кто дал самую высокую оценку и его просят рассказать какие подводные камни он там углядел, они дискутируют раунд повторяется
    5. и на 2 или 3 раунд оценки будут почти полностью совпадать
    6. далее переходим к обсуждению задачи #N+1
    На выходе получаем:
    1. усредненную оценку, которая примерно подходит всем разработчикам
    2. в такой системе очень сложно обмануть (толко методом коллективного сговора)
    Минусы способа:
    • затратный по времени
    • не совсем точный
    • не сильно удобный
    но это лучшее что пока что придумано

    Что такое стори поинты?
    Абстрактные баллы отражающие сложность задачи. Эталонная задача - выбирается самая простая стоит 1 стори поинт. Далее чем сложнее задача, тем больше стори поинтов она стоит. Стори поинты никак не связаны со временем! Это про сложность, т.к. оценка по времени не универсальная, задачи могут быть как очень простые, но долгие, так и довольно сложные, но разрешимые за относительно короткое время. 

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

    Еще оценка по времени не учитывает риски добавляя к ней стори поинты мы понимаем что для сложной задачи риск не уложиться в срок выше. Задача в один стори поинт почти наверняка будет выполнена за заданное время, задача занимающая 13 стори поинтов может иметь разлет по времени от нуля до вашей старости.

    Таск трекер
    Jira, clickup etc.
    Это чуть более продвинутый блокнот, позволяет удобно фиксировать задачи, оценки этих задач вести это в соответствии со скрамом или еще какой-то методологией. А менеджерам она удобно сводит агрегированные отчеты по производительности команды. Изучение таких штук несложно и занимает всего пару часов по любой триалке.

    Важная часть процессов это код ревью
    Это процесс когда ваш код проверяют. Обычно будет так: вам упала задача, вы сделали под нее ветку в  git, выполнили, создали merge request, чтобы ваше изменение засунули в основную ветку. При одобрении merge request возникает инерционный процесс правки по комментариям от лида.

    Правила (критически важны):
    1. никогда не воспринимайте оценку вашей работы как оценку вас
    2. вся переписка по ревью всегда внутри git сервиса, никогда ее не уводить в др мессенджеры
    3. спрашивайте до тех пор пока не поймете
    4. не спорьте чтобы спорить (если тупик, запросите помощь от рандомного коллеги для медиации)