Вычислить различия между успешными записями в Hadoop с запросами Hive
у меня есть таблица Hive, которая содержит данные вызовов клиентов. Для простоты рассмотрим, что он имеет 2 столбца, первый столбец содержит идентификатор клиента, а второй столбец содержит метку времени вызова (метку времени unix).
Я могу запросить эту таблицу, чтобы найти все звонки для каждого клиента:
SELECT * FROM mytable SORT BY customer_id, call_time;
результат:
Customer1 timestamp11
Customer1 timestamp12
Customer1 timestamp13
Customer2 timestamp21
Customer3 timestamp31
Customer3 timestamp32
...
можно ли создать запрос Hive, который возвращает для каждого клиента, начиная со второго вызова, интервал времени между двумя успешные звонки? В приведенном выше примере этот запрос должен возвращать:
Customer1 timestamp12-timestamp11
Customer1 timestamp13-timestamp12
Customer3 timestamp32-timestamp31
...
Я попытался адаптировать решения из решение sql, но я застрял с ограничениями улей: он принимает подзапросы только в FROM и соединения должны содержать только равенства.
спасибо.
EDIT1:
Я попытался использовать функцию UDF улья:
public class DeltaComputerUDF extends UDF {
private String previousCustomerId;
private long previousCallTime;
public String evaluate(String customerId, LongWritable callTime) {
long callTimeValue = callTime.get();
String timeDifference = null;
if (customerId.equals(previousCustomerId)) {
timeDifference = new Long(callTimeValue - previousCallTime).toString();
}
previousCustomerId = customerId;
previousCallTime = callTimeValue;
return timeDifference;
}}
и используйте его с название "Дельта".
но кажется (из журналов и результата), что он используется во время карты. 2 проблемы возникают из этого:
первый: перед использованием этой функции данные таблицы должны быть отсортированы по идентификатору клиента и метке времени. Запрос:
SELECT customer_id, call_time, delta(customer_id, call_time) FROM mytable DISTRIBUTE BY customer_id SORT BY customer_id, call_time;
не работает, потому что сортировочная часть выполняется в сокращенное время, долгое время после использования моей функции.
Я могу сортировать данные таблицы перед использованием функции, но Я не доволен этим, потому что это накладные расходы, я надеюсь избежать.
второй: в случае распределенной конфигурации Hadoop данные разделяются между доступными трекерами заданий. Поэтому я считаю, что будет несколько экземпляров этой функции, по одному для каждого картографа, поэтому можно разделить одни и те же данные клиента между 2 картографами. В этом случае я потеряю звонки клиентов, что неприемлемо.
Я не знаю, как решить эту вопрос. Я знаю, что DISTRIBUTE BY гарантирует, что все данные с определенным значением отправляются в один и тот же редуктор (таким образом, гарантируя, что сортировка работает должным образом), кто-нибудь знает, есть ли что-то подобное для картографа?
далее я планирую следовать предложению libjack использовать сценарий сокращения. Это "вычисление" необходимо между некоторыми другими запросами hive, поэтому я хочу попробовать все, что предлагает Hive, прежде чем перейти к другому инструменту, как предложил Balaswamy ваддеман.
EDIT2:
Я начал исследовать решение пользовательских сценариев. Но на первой странице главы 14 в книге "Программирование Улья" (в этой главе представлены пользовательские скрипты) я нашел следующий абзац:
потоковая передача обычно менее эффективна, чем кодирование сопоставимых UDFs или Предметы классы inputformat. Сериализация и десериализация данных для их передачи и выход из трубы относительно неэффективен. Также сложнее отлаживать целое программа в унифицированном виде. Однако, полезно для быстрого прототипирования и использовать существующий код, который не написан на Java. Для Hive пользователи, которые не хотят писать Java-код, могут быть очень эффективными подход.
поэтому было ясно, что пользовательские скрипты не являются лучшим решением с точки зрения эффективности.
но как мне сохранить функцию UDF, но убедитесь, что она работает так, как ожидалось в распределенном Hadoop конфигурация? Я нашел ответ на этот вопрос в разделе UDF Internals на странице wiki руководства по языку UDF. Если я напишу свой запрос:
SELECT customer_id, call_time, delta(customer_id, call_time) FROM (SELECT customer_id, call_time FROM mytable DISTRIBUTE BY customer_id SORT BY customer_id, call_time) t;
оно исполнен на уменьшите время и распределите мимо и сортируйте конструкциями гарантируйте что все показатели от такого же клиента обрабатываются таким же редуктором, в порядке звоноков.
таким образом, вышеупомянутый UDF и эта конструкция запроса решают мою проблему.
(Извините, что не добавляю ссылки, но мне не разрешено сделать это, потому что у меня недостаточно очков репутации)
3 ответов
Это старый вопрос, но для будущих ссылок я пишу здесь другое предложение:
куст оконные функции позволяет использовать предыдущие / следующие значения в запросе.
запрос кода simili может быть:
выберите customer_id, LAG (call_time, 1, 0) OVER (раздел по порядку customer_id по строкам call_time 1, предшествующим) - call_time из mytable;
вы можете использовать explicit MAP-REDUCE
с другим языком программирования, таким как Java или Python.
Где выделяют из карты {cutomer_id,call_time}
и в редукторе вы получите {customer_id,list{time_stamp}}
и в редукторе вы можете сортировать эти метки времени и обрабатывать данные.
возможно, кто-то сталкивается с аналогичным требованием, решение, которое я нашел, следующее:
1) Создайте пользовательскую функцию:
package com.example;
// imports (they depend on the hive version)
@Description(name = "delta", value = "_FUNC_(customer id column, call time column) "
+ "- computes the time passed between two succesive records from the same customer. "
+ "It generates 3 columns: first contains the customer id, second contains call time "
+ "and third contains the time passed from the previous call. This function returns only "
+ "the records that have a previous call from the same customer (requirements are not applicable "
+ "to the first call)", extended = "Example:\n> SELECT _FUNC_(customer_id, call_time) AS"
+ "(customer_id, call_time, time_passed) FROM (SELECT customer_id, call_time FROM mytable "
+ "DISTRIBUTE BY customer_id SORT BY customer_id, call_time) t;")
public class DeltaComputerUDTF extends GenericUDTF {
private static final int NUM_COLS = 3;
private Text[] retCols; // array of returned column values
private ObjectInspector[] inputOIs; // input ObjectInspectors
private String prevCustomerId;
private Long prevCallTime;
@Override
public StructObjectInspector initialize(ObjectInspector[] ois) throws UDFArgumentException {
if (ois.length != 2) {
throw new UDFArgumentException(
"There must be 2 arguments: customer Id column name and call time column name");
}
inputOIs = ois;
// construct the output column data holders
retCols = new Text[NUM_COLS];
for (int i = 0; i < NUM_COLS; ++i) {
retCols[i] = new Text();
}
// construct output object inspector
List<String> fieldNames = new ArrayList<String>(NUM_COLS);
List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(NUM_COLS);
for (int i = 0; i < NUM_COLS; ++i) {
// column name can be anything since it will be named by UDTF as clause
fieldNames.add("c" + i);
// all returned type will be Text
fieldOIs.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);
}
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
@Override
public void process(Object[] args) throws HiveException {
String customerId = ((StringObjectInspector) inputOIs[0]).getPrimitiveJavaObject(args[0]);
Long callTime = ((LongObjectInspector) inputOIs[1]).get(args[1]);
if (customerId.equals(prevCustomerId)) {
retCols[0].set(customerId);
retCols[1].set(callTime.toString());
retCols[2].set(new Long(callTime - prevCallTime).toString());
forward(retCols);
}
// Store the current customer data, for the next line
prevCustomerId = customerId;
prevCallTime = callTime;
}
@Override
public void close() throws HiveException {
// TODO Auto-generated method stub
}
}
2) Создайте банку, содержащую эту функцию. Предположим, jarname это myjar.сосуд.
3) скопируйте опарник к машине с ульем. Предположим, он помещен в /tmp
4) Определите пользовательскую функцию внутри улья:
ADD JAR /tmp/myjar.jar;
CREATE TEMPORARY FUNCTION delta AS 'com.example.DeltaComputerUDTF';
5) выполнить запрос:
SELECT delta(customer_id, call_time) AS (customer_id, call_time, time_difference) FROM
(SELECT customer_id, call_time FROM mytable DISTRIBUTE BY customer_id SORT BY customer_id, call_time) t;
Примечания:
a. Я предположил, что столбец call_time хранит данные как bigint. В случае, если это строка, в функции process мы извлекаем ее как строку (как мы делаем с customerId), а затем разбираем ее на Long
b. Я решил использовать UDTF вместо UDF, потому что таким образом он генерирует все необходимые ему данные. В противном случае (с UDF) сгенерированные данные должны быть отфильтрованы, чтобы пропустить нулевые значения. Итак, с функцией UDF (DeltaComputerUDF), описанный в первом редактировании исходного сообщения, запрос будет:
SELECT customer_id, call_time, time_difference
FROM
(
SELECT delta(customer_id, call_time) AS (customer_id, call_time, time_difference)
FROM
(
SELECT customer_id, call_time FROM mytable
DISTRIBUTE BY customer_id
SORT BY customer_id, call_time
) t
) u
WHERE time_difference IS NOT NULL;
c. Обе функции (UDF и UDTF) работают по желанию, независимо от порядка строк внутри таблицы (поэтому нет требования, чтобы данные таблицы сортировались по идентификатору клиента и времени вызова перед использованием дельта-функций)