13 заметок с тегом

BI-инструменты

Собираем данные по рекламным кампаниям ВКонтакте

В пятничном лонгриде проделаем большую работу: возьмём информацию по рекламным кампаниям ВКонтакте и сопоставим их с данными Google Analytics в Redash. Чтобы снова не поднимать сервер, будем передавать данные через Google Docs, используя Spreadsheet API.

Получение access token
Для получение пользовательского ключа ВКонтакте нужно создать приложение. Идём в раздел «Разработчики» по https://vk.com/apps?act=manage, жмём на кнопку «Создать приложение». В поле «Тип приложения» выбираем «Standalone-приложение» и даём любое название. После этого в меню слева идём в настройки и сохраняем себе ID приложения.

Актуальную информацию о ключах можно посмотреть в статье «Получение ключа доступа»

Теперь копируем себе эту ссылку:

https://oauth.vk.com/authorize?client_id=YourClientID&scope=ads&response_type=token

Но вместо YourClientID вставляем ID своего созданного приложения. В scope у этой ссылки только ads, так что с этим ключом можно будет получать только информацию о рекламном кабинете. Вставляем её в браузер и нас скидывает на другую страницу — в адресе этой странице будет указан ваш сгенерированный access token.

Срок жизни токена — 86400 секунд: ровно сутки. Чтобы получить токен без временных ограничений можно добавить в scope параметр offline. Если токен понадобилось отозвать — смените пароль от страницы или в настройках безопасности завершите активные сессии.

Ещё для запросов к API нам пригодится ID рекламного кабинета — проходим по https://vk.com/ads?act=settings и копируем «номер кабинета».

Сбор данных через запросы к API
Напишем скрипт, который обращается к серверу ВКонтакте с нашим access token и номером рекламного кабинета и берёт информацию о всех кампаниях пользователя: количество просмотров на рекламах, кликов и затрат. Затем скрипт будет формировать из него DataFrame и отправлять в Google Docs.

from oauth2client.service_account import ServiceAccountCredentials
from pandas import DataFrame
import requests
import gspread
import time

Зададим несколько константных значений: access token, ID рекламного кабинета и версию API ВКонтакте, которую будем использовать. Актуальной является версия 5.103.

token = 'fa258683fd418fafcab1fb1d41da4ec6cc62f60e152a63140c130a730829b1e0bc'
version = 5.103
id_rk = 123456789

За получение статистики по рекламе отвечает метод ads.getStatistics, но один из обязательных параметров при его вызове — ’ids’, ID рекламного объявления, статистику по которому мы хотим получить. Так как ID у нас пока нет, придётся сначала воспользоваться методов ads.getAds, который возвращает ID объявлений и кампаний.

Подробнее со всеми методами ВКонтакте API можно ознакомиться в документации

Библиотекой requests отправляем запрос к серверу и передаём свои параметры. Полученный ответ сразу переведём в формат json


campaign_ids = []
ads_ids = []
r = requests.get('https://api.vk.com/method/ads.getAds', params={
    'access_token': token,
    'v': version,
    'account_id': id_rk
})
data = r.json()['response']

Вот, как выглядит объект data: нам вернулся обычный список словарей, с которым мы уже имели дело в материале “Передаём и анализируем собранные данные по рекламным капманиям в Redash”.

Заполняем словарь ad_campaign_dict. Ключом будет ID объявления, а значением — ID кампании, к которой принадлежит объявление. Так будет удобнее присваивать к объявлению ID кампании, к которой оно принадлежало.

ad_campaign_dict = {}
for i in range(len(data)):
    ad_campaign_dict[data[i]['id']] = data[i]['campaign_id']

Теперь, имея ID каждого нужного объявления, можно обратиться к методу ads.getStatistics. Мы будем собирать количество просмотров, кликов, затрат и даты начала и конца объявления, поэтому заблаговременно заведём пустые списки.

ads_campaign_list = []
ads_id_list = []
ads_impressions_list = []
ads_clicks_list = []
ads_spent_list = []
ads_day_start_list = []
ads_day_end_list = []

Вызывать getStatistics нужно отдельно для каждого объявления — будем делать это в итераторе по ad_campaign_dict. Отправляем запрос, передавая в ‘period’ значение ‘overall’ — берём данные за всё время. У некоторых объявлений могут отсутствовать данные по полю «Просмотры» или «Клики» если они не были запущены, и, потребовав их, мы словим KeyError — во избежание этого добавим обработчик try — except, который заставит скрипт не обращать внимания на эту ошибку.

for ad_id in ad_campaign_dict:
        r = requests.get('https://api.vk.com/method/ads.getStatistics', params={
            'access_token': token,
            'v': version,
            'account_id': id_rk,
            'ids_type': 'ad',
            'ids': ad_id,
            'period': 'overall',
            'date_from': '0',
            'date_to': '0'
        })
        try:
            data_stats = r.json()['response']
            for i in range(len(data_stats)):
                for j in range(len(data_stats[i]['stats'])):
                    ads_impressions_list.append(data_stats[i]['stats'][j]['impressions'])
                    ads_clicks_list.append(data_stats[i]['stats'][j]['clicks'])
                    ads_spent_list.append(data_stats[i]['stats'][j]['spent'])
                    ads_day_start_list.append(data_stats[i]['stats'][j]['day_from'])
                    ads_day_end_list.append(data_stats[i]['stats'][j]['day_to'])
                    ads_id_list.append(data_stats[i]['id'])
                    ads_campaign_list.append(ad_campaign_dict[ad_id])
        except KeyError:
            continue

Теперь сформируем из списков DataFrame и выведем первые 5 элементов:

df = DataFrame()
df['campaign_id'] = ads_campaign_list
df['ad_id'] = ads_id_list
df['impressions'] = ads_impressions_list
df['clicks'] = ads_clicks_list
df['spent'] = ads_spent_list
df['day_start'] = ads_day_start_list
df['day_end'] = ads_day_end_list
print(df.head())

Экспорт данных в Google Docs
Для экспорта DataFrame в таблицу Google Sheets необходим ключ доступа Google API. Пройдём по https://console.developers.google.com и создадим новый проект. Даём ему любое имя и в Dashboard жмём на кнопку “Подключить API и сервисы”. Нужно включить два API — Google Drive API и Google Sheets API. Ищем первый в поиске, нажимаем на “Включить API”, затем ищем второй и проделываем то же самое.

После включения нас отправят на панель управления API. Жмём на «Создать учётные данные» — по ним будем проводить авторизацию в скрипте. Отмечаем, что используем Google Sheets API из веб-сервера и обращаемся к данным пользователя. Нажимаем на «Выбрать тип учётных данных» и создаем сервисный аккаунт. В поле «Роль» выбираем Проект — Редактор, а тип ключа оставим JSON.

После этого нам отправят файл в формате JSON с нашими учетными данными — назовём его «credentials.json» — и перенаправят на страницу с сервисными аккаунтами. Ниже будет поле с почтой — копируем её себе.

Переходим по https://docs.google.com/spreadsheets и создаем пустой файл с названием data, в который будут отправляться данные из DataFrame. В настройках доступа даём доступ по почте, скопированной ранее из сервисных аккаунтов — от неё будут приходить данные из скрипта.

Закинем файл credentials.json в директорию со скриптом и продолжим писать код. Перечисляем область видимости в виде ссылок:

scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']

И при помощи библиотек oauth2client и gspread проводим авторизацию методами ServiceAccountCredentials.from_json_keyfile_name и gspread.authorize, указывая в параметрах первого наш файл и переменную scope. Через переменную sheet будем обращаться к нашему файлу в Google Docs.

creds = ServiceAccountCredentials.from_json_keyfile_name('credentials.json', scope)
client = gspread.authorize(creds)
sheet = client.open('data').sheet1

Для ввода значений в ячейку таблички есть метод update_cell. Важно: нумерация индексов ячеек при обращении начинается не с нуля, а с единицы. Первым циклом пройдём по первой строке и перенесем туда заголовки нашего DataFrame. Во втором будем идти по каждой ячейке и вставлять соответствующие значения DataFrame. По умолчанию стоит ограничение — 100 запросов в 100 секунд. Это ограничение может остановить наш скрипт на полпути: чтобы избежать ошибки пропишем time.sleep, чтобы после каждой вставки скрипт секунду выжидал.

count_of_rows = len(df)
count_of_columns = len(df.columns)
for i in range(count_of_columns):
    sheet.update_cell(1, i + 1, list(df.columns)[i])
for i in range(1, count_of_rows + 1):
    for j in range(count_of_columns):
        sheet.update_cell(i + 1, j + 1, str(df.iloc[i, j]))
        time.sleep(1)

Если всё сделаем правильно — получим таблицу такого вида:

Экспорт данных в Redash

Подключение Google Analytics к Redash описано в статье «Как подключить Google Analytics как Redash?».

Имея в Redash таблицу с Google Analytics и рекламным кампаниям ВКонтакте, можем сопоставить их друг другу. Напишем такой запрос:

SELECT
    query_50.day_start,
    CASE WHEN ga_source LIKE '%vk%' THEN 'vk.com' END AS source,
    query_50.spent,
    query_50.impressions,
    query_50.clicks,
    SUM(query_49.ga_sessions) AS sessions,
    SUM(query_49.ga_newUsers) AS users
FROM query_49
JOIN query_50
ON query_49.ga_date = query_50.day_start
WHERE query_49.ga_source LIKE '%vk%' AND DATE(query_49.ga_date) BETWEEN '2020-05-16' AND '2020-05-20'
GROUP BY query_49.ga_date, source

ga_source — источник, с которого человек пришел на сайт. Всё, что похоже на vk оператором CASE объединяем в столбец «vk.com». Оператором JOIN добавляем таблицу с данными из ВКонтакте, объединяя по полю даты. Отсеиваем данные — возьмём день последней рекламной кампании и посмотрим на несколько дней после него. На выходе получим таблицу такого вида:

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

Передаём и анализируем собранные данные по рекламным кампаниям в Redash

В прошлый раз мы узнали, как получить полную информацию по рекламным кампаниям Facebook. А для анализа этой информации можно загрузить данные в Redash. Чтобы наш скрипт передавал данные в Redash, загрузим его в облако AWS и поднимем сервер на aiohttp. Но сперва необходимо немного улучшить наш скрипт.

from facebook_business.api import FacebookAdsApi
from facebook_business.adobjects.adaccount import AdAccount
from facebook_business.adobjects.adreportrun import AdReportRun
from facebook_business.adobjects.adsinsights import AdsInsights
from facebook_business.adobjects.adaccountuser import AdAccountUser as AdUser
from facebook_business.exceptions import FacebookRequestError
import time

Redash, к примеру, принимает данные в формате json. Ранее мы не разбирали этот источник данных, сегодня изучим и его. Файл формата json выглядит как список словарей — его наш новый скрипт и будет передавать в Redash, предварительно переведя в json. Из прошлого скрипта нам понадобятся переменная с полями, access token, app id, app secret и две функции: wait_for_async_job() и get_insights(). Вторая функция принимает все параметры аккаунта и асинхронно собирает информацию по рекламным кампаниям — из них мы выбираем поля клики, просмотры, затраты и даты.

В материале используется практически весь скрипт из статьи «Собираем данные по рекламным кампаниям Facebook»

Получение данных обернём в функцию return_json() — она будет вызывать get_insights(), заполняя список insights_lists. Так как рекламных кампаний в аккаунте может быть несколько, наш результат окажется не списком словарей, а списком списков словарей. Для этого используем простое лямбда-выражение, «сглаживающее» список списков в список. Затем возвращаем наш insights_lists_flatten.

def return_json():
   insights_lists = []
   date_preset = 'last_year'
   for elem in my_accounts:
       elem_insights = get_insights(elem, date_preset)
       insights_lists.append(elem_insights)
   flatten = lambda lst: [item for sublist in lst for item in sublist]
   insights_lists_flatten = flatten(insights_lists)
   return insights_lists_flatten

Теперь наш скрипт возвращает список словарей с информацией. Но ещё нам нужен сервер на aiohttp, который будет собирать список и возвращать его в формате json. Создадим новый файл, импортируем библиотеку aiohttp и функцию get_json() из нашего прошлого скрипта. Напишем простой обработчик запросов — данные из Facebook скрипт получает асинхронно, так что асинхронная функция handler будет «спать», пока вся информация не будет собрана и передана. Возвращает функция данные через json_response, чтобы информация передавалась в формате json.

from aiohttp import web
from get_json import return_json
 
async def handler(request):
   data = return_json()
   return web.json_response(data)

Далее инициализируем и запускаем наше приложение.

app = web.Application()
app.add_routes([web.get('/json', handler)])
web.run_app(app)

Теперь идём на AWS, создаём папку и через sftp кидаем туда оба скрипта. Проверим, что в ACL на AWS нужный порт открыт. Заходим в console — network & security — security groups — default.

Запускаем файл с сервером. После запуска можно проверить, работает ли скрипт, обратившись к нему по ip сервера с портом 0880 на route, указанный в скрипте с сервером — там мы указали /json.

Теперь обратимся через Redash к URL и получим ту самую таблицу, которую вернул скрипт:

url: ip сервера

Сформировав query results, можем написать запрос следующего вида:

select date_start, sum(clicks) as clicks, sum(impressions) as impressions, sum(spend) as spend from query_45
group by date_start

Получаем такую таблицу, сгруппированную по колонке date_start:

Теперь по этой таблице можно отобразить данные на графике — посмотрим, как количество затрат на рекламу влияет на количество кликов по ней:

Готово! В следующий раз поработаем с ВКонтакте — получим информацию по рекламным кампаниям уже оттуда.

 Нет комментариев    71   1 мес   BI-инструменты   Data analytics   redash

Когортный анализ в Redash

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

Вместе с этим когорты можно определить исходя и из других параметров: география пользователя, источник трафика, платформа устройства и другие важные параметры вашего продукта.

Мы разберемся с тем, как сравнить Retention пользователей недельных когорт в Redash, поскольку у Redash имеется специальный тип визуализации для построения такого отчета.
Определимся для начала c SQL-запросом. У нас как и прежде две таблицы — user (id пользователя и время установки приложения) и client_session — таймстемпы (created_at) активности каждого пользователя (user_id). Будем считать Retention первых семи дней для недельных когорт за последние 60 дней.
Запрос написан в Cloudera Impala, рассмотрим его.

Для начала построим общую численность когорт:

select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
	ndv(distinct user.id) as cohort_size //считаем количество пользователей в когорте
	from user 
	where from_unixtime(user.installed_at) between date_add(now(), -60) and now() //берем зарегистрированных за последние 60 дней
group by trunc(from_unixtime(user.installed_at), "WW")

Вторая часть запроса поможет посчитать количество активных пользователей на каждый день в течение первых тридцати:

select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
        datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) as days,
	ndv(distinct user.id) as value  //считаем количество активных пользователей на каждый день
		from user 
		left join client_session cs on user.id=cs.user_id
where from_unixtime(user.installed_at) between date_add(now(), -60) and now()
and from_unixtime(cs.created_at) >= date_add(now(), -60) //берем сессии за последние 60 дней
and datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) between 0 and 30 //отрезаем только первые 30 дней активности
group by 1,2

Итого запрос целиком:

select size.cohort_week, size.cohort_size, ret.days, ret.value from
(select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
		ndv(distinct user.id) as cohort_size 
	from user 
	where from_unixtime(user.installed_at) between date_add(now(), -60) and now()
group by trunc(from_unixtime(user.installed_at), "WW")) size
left join (select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
        datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) as days,
		ndv(distinct user.id) as value 
		from user 
		left join client_session cs on user.id=cs.user_id
where from_unixtime(user.installed_at) between date_add(now(), -60) and now()
and from_unixtime(cs.created_at) >= date_add(now(), -60)
and datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) between 0 and 30
group by 1,2) ret on size.cohort_week=ret.cohort_week

Отлично, теперь нам доступны правильно посчитанные данные.

Данные когорт в табличном виде

Создадим новую визуализацию в Redash и правильно укажем параметры:

Важно правильно указать параметры — им соответствуют колонки результирующего запроса

Обязательно отметим, что у нас недельные когорты:

Вуа-ля, наша визуализация когорт готова:

К ней можно добавить фильтры и параметры и использовать в дашборде

Материалы по теме

 Нет комментариев    63   4 мес   BI-инструменты   redash   sql   визуализация

Использование параметров в Redash

Самое удобное и полезное в Redash — параметры. Параметры могут быть в отчете и дашбордах.
Параметр — элемент интерфейса, которым управляет пользователь при запуске отчета.

Параметр в отчете может быть следующих типов:

Про каждый тип по отдельности:

  • Text — текстовое поле свободного ввода, может использоваться в конструкциях типа LIKE, параметр такого типа не может использоваться в публичных дашбордах
  • Number — число, которое вводит пользователь
  • Dropdown list — список значений, из которых пользователь сможет выбрать одно или несколько (недавно появилась возможность мульти-ввода параметров в выпадающим списке)
  • Query dropdown list — аналогично предыдущему, однако значения будут браться из имеющегося запроса
  • Date / Date and Time / Date and Time (with seconds) — поля ввода даты
  • Date Range / Date and Time Range / Date and Time Range (with seconds) — поля ввода интервалов дат. Удобно использовать для конструкции вида
between '{{parameter.start}}' and '{{parameter.end}}'

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

Из неочевидных, но крайне полезных решений: как сделать параметр отключаемым?
Предположим, что параметр типа «выпадающий список» называется parameter и мы хотим его настроить для столбца geo таблицы, тогда в коде запроса будет что-то типа:

WHERE
    ('{{parameter}}' = 'Disabled' or geo = '{{parameter}}')

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

 Нет комментариев    55   5 мес   BI-инструменты   parameter   redash

Обзор Looker

Сегодня поговорим о BI-платформе Looker, над которой мне удалось поработать в течение 2019-го года.

Представляю краткое содержание статьи для удобной и быстрой навигации:

  1. Что такое Looker?
  2. Как и к каким СУБД можно подключиться через Looker?
  3. Построение Looker ML модели данных
  4. Режим Explore (исследование данных на построенной модели)
  5. Построение отчетов и сохранение их в Look
  6. Примеры дашбордов в Looker

Что такое Looker?

Создатели Looker позиционируют его как программное обеспечение класса business intelligence и платформу big data аналитики, которая помогает исследовать, анализировать и делиться аналитикой бизнеса в режиме реального времени.
Looker — это действительно удобный инструмент и один из немногих продуктов класса BI, который позволяет в режиме онлайн работать с преднастроенными кубами данных (на самом деле, реляционными таблицами, которые описаны в Look ML-модели).
Инженеру, работающему над Looker, требуется описать модель данных на языке Look ML (что-то среднее между CSS и SQL), опубликовать эту модель данных и далее настроить отчетность и дашборды.
Сам Look ML достаточно прост, взаимосвязи между объектами данных задаются data-инженером, что впоследствии позволяет использовать данные без знания языка SQL (если быть точным: движок Looker сам за пользователя генерирует код на языке SQL).

Буквально недавно, в июне 2019-го года Google объявил о покупке платформы Looker за $2.6 млрд.

Как и к каким СУБД можно подключиться через Looker?

Выбор СУБД, с которыми работает Looker довольно богатый. На скриншоте ниже перечислены всевозможные подключения на Октябрь 2019 г.:

Доступные СУБД для подключения

Настроить подключение к базе данных несложно через веб-интерфейс:

Веб-интерфейс подключения к СУБД

В вопросе соединений с базами данных отдельно хочется отметить два факта: к сожалению, на текущий момент и в ближайшем будущем отсутствует поддержка Clickhouse от Яндекса. Скорее всего поддержка не появится, учитывая тот факт, что Looker был приобретен конкурирующей компанией Google.
Второй досадный факт состоит в том, что построить одну модель данных, которая бы обращалась в разные СУБД нельзя. В Looker нет встроенного хранилища, которое могло бы объединять результаты запроса (кстати, в отличии даже от того же Redash).
Это означает, что аналитическая архитектура данных должна быть построена в рамках одной СУБД (желательно с высоким быстродействием или на агрегированных данных).

Построение Looker ML модели данных

Для того чтобы построить отчет или дашборд в Looker, предварительно необходимо настроить модель данных. Синтаксис языка Look ML достаточно подробно описан в документации. От себя могу добавить, что описание модели не требует долгого погружения для специалиста со знанием SQL. Скорее, необходимо перестроить подход к подготовке модели данных. Язык Look ML очень похож на CSS:

Консоль создания Look ML модели

В модели данных прописываются связи с таблицами, ключи, гранулярность, информация о том, какие поля являются фактами, какие измерениями. Для фактов прописывается агрегация. Разумеется, при создании модели можно использовать различные IF / CASE выражения.

Режим Explore

Наверное, это самая главная киллер-фича Looker, поскольку позволяет любым бизнес-поздразделениям самостоятельно получать данные без привлечения аналитиков / инженеров данных. И, видимо, поэтому использование аккаунтов с использованием режиме Explore тарифицируется отдельно.

Фактически, режим Explore это интерфейс, который позволяет использовать настроенную Look ML модель данных, выбрать необходимые метрики и измерения и построить кастомный отчет / визуализацию.
К примеру, мы хотим понять сколько каких действий в интерфейсе Лукера было совершено на прошлой неделе. Для этого, используя режим Explore, мы выберем поле Date и поставим на него фильтр: прошлая неделя (Looker в этом смысле достаточно умный и в фильтре будет достаточно написать ‘Last week’), затем из измерений выберем «Категорию» и в качестве метрики — Количество. После нажатия кнопки Run сформируется готовый отчет.

Построение отчета в Looker

Затем, используя полученные данные в табличной форме можно настроить визуализацию любого вида. Например, Pie chart:

Применение визуализации к отчету

Построение отчетов и сохранение их в Look

Полученный набор данных / визуализацию в режиме Explore иногда хочется сохранить и поделиться с коллегами, для этого в Looker существует отдельная сущность — Look. Это готовый построенный отчет с выбранными фильтрами / измерениями / фактами.

Пример сохраненного Look

Примеры дашбордов в Looker

Систематизируя склад созданных Look зачастую хочется получить готовую композицию / overview ключевых метрик, которые было бы видно на одном листе.
Для этих целей отлично подходит создание дашборда. Дашборд создается либо на лету, либо используя ранее созданные Look. Одной из «фишек» дашборда является установка параметров, которые меняются на всем дашборде и могут быть применены ко всем Look сразу.

Интересные фишки одной строкой

  • В Looker можно ссылаться на другие отчеты и, используя данных функционал, можно создать динамический параметр, который передается по ссылке.
    Например, построили отчет с разделениям выручки по странам и в этот отчете можем ссылаться на дашборд по отдельно взятой стране. Переходя по ссылке, пользователь видит дашборд по конкретной стране, на которую перешел
  • На каждой странице Looker существует чат, в котором оперативно отвечает поддержка
  • Looker не умеет работать с merge данных на уровне разных СУБД, однако может объединить данные на уровне готовых Look (в нашем случае эта функциональность работает очень странно).
  • В рамках работы с различными моделями данных, я обнаружил крайне нетривиальное использование SQL для подсчета уникальных значений в ненормализованный таблице данных, Looker называет это симметричными агрегатами.
    SQL действительно выглядит очень нетривиально:
SELECT 
 order_items.order_id AS "order_items.order_id",
 order_items.sale_price AS "order_items.sale_price",
 (COALESCE(CAST( ( SUM(DISTINCT (CAST(FLOOR(COALESCE(users.age ,0)
 *(1000000*1.0)) AS DECIMAL(38,0))) + 
 CAST(STRTOL(LEFT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0))
 * 1.0e8 + CAST(STRTOL(RIGHT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0)) ) 
 - SUM(DISTINCT CAST(STRTOL(LEFT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0))
 * 1.0e8 + CAST(STRTOL(RIGHT(MD5(CONVERT(VARCHAR,users.id )),15),16) AS DECIMAL(38,0))) ) 
 AS DOUBLE PRECISION) 
 / CAST((1000000*1.0) AS DOUBLE PRECISION), 0) 
 / NULLIF(COUNT(DISTINCT CASE WHEN users.age IS NOT NULL THEN users.id 
 ELSE NULL END), 0)) AS "users.average_age"
FROM order_items AS order_items
LEFT JOIN users AS users ON order_items.user_id = users.id

GROUP BY 1,2
ORDER BY 3 DESC
LIMIT 500
  • При внедрении Looker к покупке обязателен JumpStart Kit, который стоит не менее $6k, в рамках которого вы получаете поддержку и консультации от Looker при внедрении инструмента.
 Нет комментариев    88   5 мес   analysis   BI-инструменты   Data engineering   looker   sql
Ранее Ctrl + ↓