Merge pull request #3270 from yandex/left-join-right-keys-fix

Left join right keys fix
This commit is contained in:
alexey-milovidov 2018-10-07 23:26:40 +03:00 committed by GitHub
commit 36c3feea80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 265 additions and 78 deletions

View File

@ -125,9 +125,6 @@ void CreatingSetsBlockInputStream::createOne(SubqueryForSet & subquery)
if (!done_with_join)
{
if (subquery.joined_block_actions)
subquery.joined_block_actions->execute(block);
for (const auto & name_with_alias : subquery.joined_block_aliases)
{
if (block.has(name_with_alias.first))
@ -140,6 +137,9 @@ void CreatingSetsBlockInputStream::createOne(SubqueryForSet & subquery)
}
}
if (subquery.joined_block_actions)
subquery.joined_block_actions->execute(block);
if (!subquery.join->insertFromBlock(block))
done_with_join = true;
}

View File

@ -285,7 +285,7 @@ static std::vector<ASTTableExpression> getTableExpressions(const ASTPtr & query)
{
ASTSelectQuery * select_query = typeid_cast<ASTSelectQuery *>(query.get());
std::vector<ASTTableExpression> tables_expression;
std::vector<ASTTableExpression> table_expressions;
if (select_query && select_query->tables)
{
@ -294,11 +294,11 @@ static std::vector<ASTTableExpression> getTableExpressions(const ASTPtr & query)
ASTTablesInSelectQueryElement & select_element = static_cast<ASTTablesInSelectQueryElement &>(*element);
if (select_element.table_expression)
tables_expression.emplace_back(static_cast<ASTTableExpression &>(*select_element.table_expression));
table_expressions.emplace_back(static_cast<ASTTableExpression &>(*select_element.table_expression));
}
}
return tables_expression;
return table_expressions;
}
void ExpressionAnalyzer::translateQualifiedNames()
@ -908,27 +908,40 @@ void ExpressionAnalyzer::normalizeTree()
if (!settings.asterisk_left_columns_only)
{
auto columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(context, select_query).getNames();
all_columns_name.insert(all_columns_name.end(), columns_from_joined_table.begin(), columns_from_joined_table.end());
auto columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query);
for (auto & column : columns_from_joined_table)
all_columns_name.emplace_back(column.name_and_type.name);
}
if (all_columns_name.empty())
throw Exception("Logical error: an asterisk cannot be replaced with empty columns.", ErrorCodes::LOGICAL_ERROR);
throw Exception("An asterisk cannot be replaced with empty columns.", ErrorCodes::LOGICAL_ERROR);
TableNamesAndColumnsName table_names_nad_columns_name;
TableNamesAndColumnsName table_names_and_columns_name;
if (select_query && select_query->tables && !select_query->tables->children.empty())
{
std::vector<ASTTableExpression> tables_expression = getTableExpressions(query);
bool first = true;
for (const auto & table_expression : tables_expression)
{
const auto table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
NamesAndTypesList names_and_types = getNamesAndTypeListFromTableExpression(table_expression, context);
table_names_nad_columns_name.emplace_back(std::pair(table_name, names_and_types.getNames()));
if (!first)
{
/// For joined tables qualify duplicating names.
for (auto & name_and_type : names_and_types)
if (source_columns.contains(name_and_type.name))
name_and_type.name = table_name.getQualifiedNamePrefix() + name_and_type.name;
}
first = false;
table_names_and_columns_name.emplace_back(std::pair(table_name, names_and_types.getNames()));
}
}
QueryNormalizer(query, aliases, settings, all_columns_name, table_names_nad_columns_name).perform();
QueryNormalizer(query, aliases, settings, all_columns_name, table_names_and_columns_name).perform();
}
@ -2268,7 +2281,8 @@ void ExpressionAnalyzer::addJoinAction(ExpressionActionsPtr & actions, bool only
}
void ExpressionAnalyzer::AnalyzedJoin::createJoinedBlockActions(const ASTSelectQuery * select_query_with_join,
void ExpressionAnalyzer::AnalyzedJoin::createJoinedBlockActions(const NamesAndTypesList & source_columns,
const ASTSelectQuery * select_query_with_join,
const Context & context)
{
if (!select_query_with_join)
@ -2291,20 +2305,28 @@ void ExpressionAnalyzer::AnalyzedJoin::createJoinedBlockActions(const ASTSelectQ
NameSet required_columns_set(key_names_right.begin(), key_names_right.end());
for (const auto & joined_column : columns_added_by_join)
required_columns_set.insert(joined_column.original_name);
required_columns_set.insert(joined_column.name_and_type.name);
Names required_columns(required_columns_set.begin(), required_columns_set.end());
required_columns_set.insert(key_names_right.begin(), key_names_right.end());
const auto & columns_from_joined_table = getColumnsFromJoinedTable(source_columns, context, select_query_with_join);
NamesAndTypesList source_column_names;
for (auto & column : columns_from_joined_table)
source_column_names.emplace_back(column.name_and_type);
required_columns_from_joined_table.insert(required_columns_from_joined_table.end(),
required_columns_set.begin(), required_columns_set.end());
const auto & source_columns_name = getColumnsFromJoinedTable(context, select_query_with_join);
ExpressionAnalyzer analyzer(expression_list, context, nullptr, source_columns_name, required_columns_from_joined_table);
ExpressionAnalyzer analyzer(expression_list, context, nullptr, source_column_names, required_columns);
joined_block_actions = analyzer.getActions(false);
for (const auto & column_required_from_actions : joined_block_actions->getRequiredColumns())
if (!required_columns_set.count(column_required_from_actions))
required_columns_from_joined_table.push_back(column_required_from_actions);
auto required_action_columns = joined_block_actions->getRequiredColumns();
required_columns_from_joined_table.insert(required_action_columns.begin(), required_action_columns.end());
auto sample = joined_block_actions->getSampleBlock();
for (auto & column : key_names_right)
if (!sample.has(column))
required_columns_from_joined_table.insert(column);
for (auto & column : columns_added_by_join)
if (!sample.has(column.name_and_type.name))
required_columns_from_joined_table.insert(column.name_and_type.name);
}
@ -2317,15 +2339,28 @@ NamesAndTypesList ExpressionAnalyzer::AnalyzedJoin::getColumnsAddedByJoin() cons
return result;
}
NamesAndTypesList ExpressionAnalyzer::AnalyzedJoin::getColumnsFromJoinedTable(const Context & context, const ASTSelectQuery * select_query_with_join)
const ExpressionAnalyzer::AnalyzedJoin::JoinedColumnsList & ExpressionAnalyzer::AnalyzedJoin::getColumnsFromJoinedTable(
const NamesAndTypesList & source_columns, const Context & context, const ASTSelectQuery * select_query_with_join)
{
if (select_query_with_join && !columns_from_joined_table.size())
if (select_query_with_join && columns_from_joined_table.empty())
{
if (const ASTTablesInSelectQueryElement * node = select_query_with_join->join())
{
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
auto table_name_with_alias = getTableNameWithAliasFromTableExpression(table_expression, context);
columns_from_joined_table = getNamesAndTypeListFromTableExpression(table_expression, context);
auto columns = getNamesAndTypeListFromTableExpression(table_expression, context);
for (auto & column : columns)
{
columns_from_joined_table.emplace_back(column, column.name);
if (source_columns.contains(column.name))
{
auto qualified_name = table_name_with_alias.getQualifiedNamePrefix() + column.name;
columns_from_joined_table.back().name_and_type.name = qualified_name;
}
}
}
}
@ -2411,22 +2446,23 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
else if (table_to_join.database_and_table_name)
table = table_to_join.database_and_table_name;
auto interpreter = interpretSubquery(table, context, subquery_depth, analyzed_join.required_columns_from_joined_table);
Names original_columns;
for (const auto & column : analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query))
if (analyzed_join.required_columns_from_joined_table.count(column.name_and_type.name))
original_columns.emplace_back(column.original_name);
auto interpreter = interpretSubquery(table, context, subquery_depth, original_columns);
subquery_for_set.source = std::make_shared<LazyBlockInputStream>(
interpreter->getSampleBlock(),
[interpreter]() mutable { return interpreter->execute().in; });
}
/// Alias duplicating columns.
for (const auto & joined_column : analyzed_join.columns_added_by_join)
{
const auto & qualified_name = joined_column.name_and_type.name;
if (joined_column.original_name != qualified_name)
subquery_for_set.joined_block_aliases.emplace_back(joined_column.original_name, qualified_name);
}
/// Alias duplicating columns as qualified.
for (const auto & column : analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query))
if (analyzed_join.required_columns_from_joined_table.count(column.name_and_type.name))
subquery_for_set.joined_block_aliases.emplace_back(column.original_name, column.name_and_type.name);
auto sample_block = subquery_for_set.source->getHeader();
analyzed_join.joined_block_actions->execute(sample_block);
for (const auto & name_with_alias : subquery_for_set.joined_block_aliases)
{
if (sample_block.has(name_with_alias.first))
@ -2439,6 +2475,8 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
}
}
analyzed_join.joined_block_actions->execute(sample_block);
/// TODO You do not need to set this up when JOIN is only needed on remote servers.
subquery_for_set.join = join;
subquery_for_set.join->setSampleBlock(sample_block);
@ -2836,7 +2874,7 @@ void ExpressionAnalyzer::collectUsedColumns()
analyzed_join.columns_added_by_join.erase(it++);
}
analyzed_join.createJoinedBlockActions(select_query, context);
analyzed_join.createJoinedBlockActions(source_columns, select_query, context);
/// Some columns from right join key may be used in query. This columns will be appended to block during join.
for (const auto & right_key_name : analyzed_join.key_names_right)
@ -2956,8 +2994,8 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr()
return table_belonging;
};
std::function<void(ASTPtr &, const DatabaseAndTableWithAlias &)> translate_qualified_names;
translate_qualified_names = [&](ASTPtr & ast, const DatabaseAndTableWithAlias & source_names)
std::function<void(ASTPtr &, const DatabaseAndTableWithAlias &, bool)> translate_qualified_names;
translate_qualified_names = [&](ASTPtr & ast, const DatabaseAndTableWithAlias & source_names, bool right_table)
{
auto * identifier = typeid_cast<const ASTIdentifier *>(ast.get());
if (identifier)
@ -2966,12 +3004,16 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr()
{
auto num_components = getNumComponentsToStripInOrderToTranslateQualifiedName(*identifier, source_names);
stripIdentifier(ast, num_components);
if (right_table && source_columns.contains(ast->getColumnName()))
source_names.makeQualifiedName(ast);
}
return;
}
for (auto & child : ast->children)
translate_qualified_names(child, source_names);
translate_qualified_names(child, source_names, right_table);
};
const auto supported_syntax = " Supported syntax: JOIN ON Expr([table.]column, ...) = Expr([table.]column, ...) "
@ -3001,8 +3043,8 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr()
auto add_join_keys = [&](ASTPtr & ast_to_left_table, ASTPtr & ast_to_right_table)
{
translate_qualified_names(ast_to_left_table, left_source_names);
translate_qualified_names(ast_to_right_table, right_source_names);
translate_qualified_names(ast_to_left_table, left_source_names, false);
translate_qualified_names(ast_to_right_table, right_source_names, true);
analyzed_join.key_asts_left.push_back(ast_to_left_table);
analyzed_join.key_names_left.push_back(ast_to_left_table->getColumnName());
@ -3058,8 +3100,18 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns)
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
auto joined_table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
auto add_name_to_join_keys = [](Names & join_keys, ASTs & join_asts, const String & name, const ASTPtr & ast)
auto add_name_to_join_keys = [&](Names & join_keys, ASTs & join_asts, const ASTPtr & ast, bool right_table)
{
String name;
if (right_table)
{
name = ast->getAliasOrColumnName();
if (source_columns.contains(name))
name = joined_table_name.getQualifiedNamePrefix() + name;
}
else
name = ast->getColumnName();
join_keys.push_back(name);
join_asts.push_back(ast);
};
@ -3069,8 +3121,8 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns)
auto & keys = typeid_cast<ASTExpressionList &>(*table_join.using_expression_list);
for (const auto & key : keys.children)
{
add_name_to_join_keys(analyzed_join.key_names_left, analyzed_join.key_asts_left, key->getColumnName(), key);
add_name_to_join_keys(analyzed_join.key_names_right, analyzed_join.key_asts_right, key->getAliasOrColumnName(), key);
add_name_to_join_keys(analyzed_join.key_names_left, analyzed_join.key_asts_left, key, false);
add_name_to_join_keys(analyzed_join.key_names_right, analyzed_join.key_asts_right, key, true);
}
}
else if (table_join.on_expression)
@ -3081,29 +3133,26 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns)
/// If we use USING syntax, join_key_names_left and join_key_names_right are almost the same, but we need to use
/// join_key_names_right in order to support aliases in USING list. Example:
/// SELECT x FROM tab1 ANY LEFT JOIN tab2 USING (x as y) - will join column x from tab1 with column y from tab2.
auto & not_joined_columns = table_join.using_expression_list ? analyzed_join.key_names_right : analyzed_join.key_names_left;
auto columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(context, select_query);
//auto & not_joined_columns = table_join.using_expression_list ? analyzed_join.key_names_right : analyzed_join.key_names_left;
auto & columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query);
for (auto & column_name_and_type : columns_from_joined_table)
for (auto & column : columns_from_joined_table)
{
auto & column_name = column_name_and_type.name;
auto & column_type = column_name_and_type.type;
if (not_joined_columns.end() == std::find(not_joined_columns.begin(), not_joined_columns.end(), column_name))
auto & column_name = column.name_and_type.name;
auto & column_type = column.name_and_type.type;
auto & original_name = column.original_name;
//if (table_join.on_expression
// || not_joined_columns.end() == std::find(not_joined_columns.begin(), not_joined_columns.end(), column_name))
{
auto qualified_name = column_name;
/// Change name for duplicate column form joined table.
if (source_columns.contains(qualified_name))
qualified_name = joined_table_name.getQualifiedNamePrefix() + qualified_name;
if (joined_columns.count(qualified_name)) /// Duplicate columns in the subquery for JOIN do not make sense.
if (joined_columns.count(column_name)) /// Duplicate columns in the subquery for JOIN do not make sense.
continue;
joined_columns.insert(qualified_name);
joined_columns.insert(column_name);
bool make_nullable = settings.join_use_nulls && (table_join.kind == ASTTableJoin::Kind::Left ||
table_join.kind == ASTTableJoin::Kind::Full);
auto type = make_nullable ? makeNullable(column_type) : column_type;
analyzed_join.columns_added_by_join.emplace_back(NameAndTypePair(qualified_name, std::move(type)), column_name);
analyzed_join.columns_added_by_join.emplace_back(NameAndTypePair(column_name, std::move(type)), original_name);
}
}
}

View File

@ -239,7 +239,7 @@ private:
* It's possible to use name `expr(t2 columns)`.
*/
Names key_names_left;
Names key_names_right;
Names key_names_right; /// Duplicating names are qualified.
ASTs key_asts_left;
ASTs key_asts_right;
@ -256,10 +256,11 @@ private:
using JoinedColumnsList = std::list<JoinedColumn>;
/// All columns which can be read from joined table.
NamesAndTypesList columns_from_joined_table;
/// Columns which will be used in query to the joined query.
Names required_columns_from_joined_table;
/// All columns which can be read from joined table. Duplicating names are qualified.
JoinedColumnsList columns_from_joined_table;
/// Columns which will be used in query to the joined query. Duplicating names are qualified.
NameSet required_columns_from_joined_table;
/// Columns which will be added to block, possible including some columns from right join key.
JoinedColumnsList columns_added_by_join;
/// Such columns will be copied from left join keys during join.
@ -267,11 +268,15 @@ private:
/// Actions which need to be calculated on joined block.
ExpressionActionsPtr joined_block_actions;
void createJoinedBlockActions(const ASTSelectQuery * select_query_with_join, const Context & context);
void createJoinedBlockActions(const NamesAndTypesList & source_columns,
const ASTSelectQuery * select_query_with_join,
const Context & context);
NamesAndTypesList getColumnsAddedByJoin() const;
NamesAndTypesList getColumnsFromJoinedTable(const Context & context, const ASTSelectQuery * select_query_with_join);
const JoinedColumnsList & getColumnsFromJoinedTable(const NamesAndTypesList & source_columns,
const Context & context,
const ASTSelectQuery * select_query_with_join);
};
AnalyzedJoin analyzed_join;

View File

@ -524,16 +524,20 @@ namespace
struct Adder<ASTTableJoin::Kind::Left, ASTTableJoin::Strictness::Any, Map>
{
static void addFound(const typename Map::const_iterator & it, size_t num_columns_to_add, MutableColumns & added_columns,
size_t /*i*/, IColumn::Filter * /*filter*/, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/,
size_t i, IColumn::Filter * filter, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/,
const std::vector<size_t> & right_indexes)
{
(*filter)[i] = 1;
for (size_t j = 0; j < num_columns_to_add; ++j)
added_columns[j]->insertFrom(*it->second.block->getByPosition(right_indexes[j]).column.get(), it->second.row_num);
}
static void addNotFound(size_t num_columns_to_add, MutableColumns & added_columns,
size_t /*i*/, IColumn::Filter * /*filter*/, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/)
size_t i, IColumn::Filter * filter, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/)
{
(*filter)[i] = 0;
for (size_t j = 0; j < num_columns_to_add; ++j)
added_columns[j]->insertDefault();
}
@ -563,9 +567,11 @@ namespace
struct Adder<KIND, ASTTableJoin::Strictness::All, Map>
{
static void addFound(const typename Map::const_iterator & it, size_t num_columns_to_add, MutableColumns & added_columns,
size_t i, IColumn::Filter * /*filter*/, IColumn::Offset & current_offset, IColumn::Offsets * offsets,
size_t i, IColumn::Filter * filter, IColumn::Offset & current_offset, IColumn::Offsets * offsets,
const std::vector<size_t> & right_indexes)
{
(*filter)[i] = 1;
size_t rows_joined = 0;
for (auto current = &static_cast<const typename Map::mapped_type::Base_t &>(it->second); current != nullptr; current = current->next)
{
@ -580,8 +586,10 @@ namespace
}
static void addNotFound(size_t num_columns_to_add, MutableColumns & added_columns,
size_t i, IColumn::Filter * /*filter*/, IColumn::Offset & current_offset, IColumn::Offsets * offsets)
size_t i, IColumn::Filter * filter, IColumn::Offset & current_offset, IColumn::Offsets * offsets)
{
(*filter)[i] = 0;
if (KIND == ASTTableJoin::Kind::Inner)
{
(*offsets)[i] = current_offset;
@ -741,8 +749,8 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
/// Used with ANY INNER JOIN
std::unique_ptr<IColumn::Filter> filter;
if ((kind == ASTTableJoin::Kind::Inner || kind == ASTTableJoin::Kind::Right) && strictness == ASTTableJoin::Strictness::Any)
filter = std::make_unique<IColumn::Filter>(rows);
bool filter_left_keys = (kind == ASTTableJoin::Kind::Inner || kind == ASTTableJoin::Kind::Right) && strictness == ASTTableJoin::Strictness::Any;
filter = std::make_unique<IColumn::Filter>(rows);
/// Used with ALL ... JOIN
IColumn::Offset current_offset = 0;
@ -771,14 +779,11 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
block.insert(ColumnWithTypeAndName(std::move(added_columns[i]), added_type_name[i].first, added_type_name[i].second));
/// If ANY INNER | RIGHT JOIN - filter all the columns except the new ones.
if (filter)
if (filter_left_keys)
for (size_t i = 0; i < existing_columns; ++i)
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->filter(*filter, -1);
/// If ALL ... JOIN - we replicate all the columns except the new ones.
if (offsets_to_replicate)
for (size_t i = 0; i < existing_columns; ++i)
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->replicate(*offsets_to_replicate);
ColumnUInt64::Ptr mapping;
/// Add join key columns from right block if they has different name.
for (size_t i = 0; i < key_names_right.size(); ++i)
@ -789,9 +794,34 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
if (needed_key_names_right.count(right_name) && !block.has(right_name))
{
const auto & col = block.getByName(left_name);
block.insert({col.column, col.type, right_name});
auto column = col.column;
if (!filter_left_keys)
{
if (!mapping)
{
auto mut_mapping = ColumnUInt64::create(column->size());
auto & data = mut_mapping->getData();
size_t size = column->size();
for (size_t j = 0; j < size; ++j)
data[j] = (*filter)[j] ? j : size;
mapping = std::move(mut_mapping);
}
auto mut_column = (*std::move(column)).mutate();
mut_column->insertDefault();
column = mut_column->index(*mapping, 0);
}
block.insert({column, col.type, right_name});
}
}
/// If ALL ... JOIN - we replicate all the columns except the new ones.
if (offsets_to_replicate)
{
for (size_t i = 0; i < existing_columns; ++i)
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->replicate(*offsets_to_replicate);
}
}

View File

@ -133,6 +133,9 @@ std::pair<String, String> getDatabaseAndTableNameFromIdentifier(const ASTIdentif
String DatabaseAndTableWithAlias::getQualifiedNamePrefix() const
{
if (alias.empty() && table.empty())
return "";
return (!alias.empty() ? alias : (database + '.' + table)) + '.';
}

View File

@ -32,3 +32,4 @@
1
-------Push to having expression, need check.-------
-------Compatibility test-------
1 2000-01-01 2000-01-01 1 test string 1 1

View File

@ -70,7 +70,7 @@ SELECT * FROM (SELECT toUInt64(b), sum(id) AS b FROM test.test) WHERE `toUInt64(
SELECT * FROM (SELECT toUInt64(table_alias.b) AS a, sum(id) AS b FROM test.test AS table_alias) AS outer_table_alias WHERE outer_table_alias.b = 3; -- { serverError 277 }
SELECT '-------Compatibility test-------';
SELECT * FROM (SELECT 1 AS id, toDate('2000-01-01') AS date FROM system.numbers LIMIT 1) ANY LEFT JOIN (SELECT * FROM test.test) AS b USING date WHERE b.date = toDate('2000-01-01'); -- {serverError 47}
SELECT * FROM (SELECT toInt8(1) AS id, toDate('2000-01-01') AS date FROM system.numbers LIMIT 1) ANY LEFT JOIN (SELECT * FROM test.test) AS b USING date, id WHERE b.date = toDate('2000-01-01');
DROP TABLE IF EXISTS test.test;
DROP TABLE IF EXISTS test.test_view;

View File

@ -0,0 +1,3 @@
1 1 1 2
1 2 1 2
2 3 0 0

View File

@ -0,0 +1,20 @@
create database if not exists test;
DROP TABLE IF EXISTS test.a1;
DROP TABLE IF EXISTS test.a2;
CREATE TABLE test.a1(a UInt8, b UInt8) ENGINE=Memory;
CREATE TABLE test.a2(a UInt8, b UInt8) ENGINE=Memory;
INSERT INTO test.a1 VALUES (1, 1);
INSERT INTO test.a1 VALUES (1, 2);
INSERT INTO test.a1 VALUES (2, 3);
INSERT INTO test.a2 VALUES (1, 2);
INSERT INTO test.a2 VALUES (1, 3);
INSERT INTO test.a2 VALUES (1, 4);
SELECT * FROM test.a1 as a left JOIN test.a2 as b on a.a=b.a ORDER BY b SETTINGS join_default_strictness='ANY';
DROP TABLE IF EXISTS test.a1;
DROP TABLE IF EXISTS test.a2;

View File

@ -0,0 +1,17 @@
1 1 1 1
2 2 0 0
-
1 1 1 1
2 2 0 0
-
1 1 1 1
2 2 0 0
-
1 1 1 1
2 2 0 0
-
1 1 1 1
2 2 0 0
-
1 1 1 1
2 2 0 0

View File

@ -0,0 +1,25 @@
create database if not exists test;
drop table if exists test.t;
drop table if exists test.s;
create table test.t(a Int64, b Int64) engine = TinyLog;
insert into test.t values(1,1);
insert into test.t values(2,2);
create table test.s(a Int64, b Int64) engine = TinyLog;
insert into test.s values(1,1);
select a, b, s_a, s_b from test.t all left join (select a,b,a s_a, b s_b from test.s) using (a,b);
select '-';
select * from test.t all left join test.s using (a,b);
select '-';
select a,b,s_a,s_b from test.t all left join (select a, b, a s_a, b s_b from test.s) s on (s.a = t.a and s.b = t.b);
select '-';
select * from test.t all left join (select a s_a, b s_b from test.s) on (s_a = t.a and s_b = t.b);
select '-';
select a,b,s_a,s_b from test.t all left join (select a,b, a s_a, b s_b from test.s) on (s_a = t.a and s_b = t.b);
select '-';
select t.*, s.* from test.t all left join test.s on (s.a = t.a and s.b = t.b);
drop table if exists test.t;
drop table if exists test.s;

View File

@ -0,0 +1,2 @@
1 1 1 1 1
2 2 0 0 0

View File

@ -0,0 +1,16 @@
create database if not exists test;
drop table if exists test.t;
drop table if exists test.z;
create table test.t(a Int64, b Int64) engine = TinyLog;
insert into test.t values(1,1);
insert into test.t values(2,2);
create table test.z(c Int64, d Int64, e Int64) engine = TinyLog;
insert into test.z values(1,1,1);
select * from test.t all left join test.z on (z.c = t.a and z.d = t.b);
drop table if exists test.t;
drop table if exists test.z;

View File

@ -0,0 +1 @@
2 2 b

View File

@ -0,0 +1,15 @@
create database if not exists test;
drop table if exists test.t;
drop table if exists test.s;
create table test.t(a Int64, b Int64, c String) engine = TinyLog;
insert into test.t values(1,1,'a'),(2,2,'b');
create table test.s(a Int64, b Int64, c String) engine = TinyLog;
insert into test.s values(1,1,'a');
select t.* from test.t all left join test.s on (s.a = t.a and s.b = t.b) where s.a = 0 and s.b = 0;
drop table if exists test.t;
drop table if exists test.s;