Hive / SQL-левое соединение с резервным вариантом
В Apache Hive у меня есть таблицы, которые я хотел бы соединить слева, сохраняя все данные из левых данных и добавляя данные, где это возможно, из правой таблицы. Для этого я использую два соединения, потому что соединение основано на двух полях (material_id и location_id). Это прекрасно работает с двумя традиционными левыми соединениями:
SELECT
a.*,
b.*
FROM a
INNER JOIN (some more complex select) b
ON a.material_id=b.material_id
AND a.location_id=b.location_id;
Для location_id база данных содержит только два различных значения, скажем 1 и 2.
Теперь у нас есть требование, что если нет " идеального соответствия", это означает, что только material_id могут быть объединены, и не существует правильного сочетания material_id и чего ты больше (например, material_id=100 а-не боитесь ли=1) для соединения для чего ты больше в б-таблица, присоединяйтесь должны "по умолчанию" или "запасной вариант" на другие возможные значения-не боитесь ли, например, material_id=001 и-не боитесь ли=2, и наоборот. Это должно иметь место только для location_id.
Мы уже рассмотрели все возможные ответы также с CASE и т. д. но безрезультатно. Установка типа
...
ON a.material_id=b.material_id AND a.location_id=
CASE WHEN a.location_id = b.location_id THEN b.location_id ELSE ...;
Мы попытались или не поняли, как на самом деле сделать в языке запросов hive.
Спасибо за вашу помощь! Может быть, у кого-то есть умная идея.
Вот некоторые примеры данных:
Table a
| material_id | location_id | other_column_a |
| 100 | 1 | 45 |
| 101 | 1 | 45 |
| 103 | 1 | 45 |
| 103 | 2 | 45 |
Table b
| material_id | location_id | other_column_b |
| 100 | 1 | 66 |
| 102 | 1 | 76 |
| 103 | 2 | 88 |
Left - Join Table
| material_id | location_id | other_column_a | other_column_b
| 100 | 1 | 45 | 66
| 101 | 1 | 45 | NULL (mat. not in b)
| 103 | 1 | 45 | DEFAULT TO where location_id=2 (88)
| 103 | 2 | 45 | 88
PS: Как сказано здесь существует и т. д. не работает в подзапросе ON.
2 ответа:
Решение состоит в том, чтобы оставить соединение без
a.location_id = b.location_id
и пронумеровать все строки в порядке предпочтения. Затем выполните фильтрацию по номеру строки. В коде ниже соединение будет дублировать строки сначала, потому что все соответствующие material_id будут объединены, затемrow_number()
Функция назначит 1 строкам, гдеa.location_id = b.location_id
и 2 строкам, гдеa.location_id <> b.location_id
Если существуют также строки, гдеa.location_id = b.location_id
и 1, если таких нет.b.location_id
добавляется к функцииorder by
в функции row_number (), поэтому она будет" предпочитать " строки с более низким значениемb.location_id
, если нет точных совмещение. Надеюсь, вы уловили мою мысль.select * from ( SELECT a.*, b.*, row_number() over(partition by material_id order by CASE WHEN a.location_id = b.location_id THEN 1 ELSE 2 END, b.location_id ) as rn FROM a LEFT JOIN (some more complex select) b ON a.material_id=b.material_id )s where rn=1 ;
Может быть, это полезно для кого-то в будущем:
Мы также придумали другой подход.
Во-первых, мы создаем другую таблицу для вычисления средних значений из таблицы b на основе material_id по всем (!) пункты назначения.
Во-вторых, в таблице join мы создаем три столбца: c1-значение, в котором material_id и location_id совпадают (результат левого соединения таблицы a с таблицей b). Этот столбец является нулевым, если нет идеального соответствия.
C2-значение из таблица, в которую мы записываем число из таблицы averages (fallback) для этого material_id (независимо от местоположения)
C3-столбец" фактическое значение", в котором мы используем оператор case, чтобы решить, является ли столбец 1 нулевым (нет идеального соответствия материала и местоположения), а затем мы используем значение из столбца 2 (среднее значение по всем другим местоположениям для материала) для дальнейших вычислений.