public override SqlFragment Visit(DbGroupByExpression expression) { // first process the input DbGroupExpressionBinding e = expression.Input; SelectStatement innerSelect = VisitInputExpressionEnsureSelect(e.Expression, e.VariableName, e.VariableType); scope.Add(e.GroupVariableName, innerSelect); SelectStatement select = WrapIfNotCompatible(innerSelect, expression.ExpressionKind); CollectionType ct = (CollectionType)expression.ResultType.EdmType; RowType rt = (RowType)ct.TypeUsage.EdmType; int propIndex = 0; foreach (DbExpression key in expression.Keys) { var fragment = key.Accept(this); select.AddGroupBy(fragment); propIndex++; var colFragment = fragment as ColumnFragment; if (colFragment != null) { colFragment = colFragment.Clone(); colFragment.ColumnAlias = String.Format("K{0}", propIndex); select.Columns.Add(colFragment); } } for (int agg = 0; agg < expression.Aggregates.Count; agg++) { DbAggregate a = expression.Aggregates[agg]; DbFunctionAggregate fa = a as DbFunctionAggregate; if (fa == null) { throw new NotSupportedException(); } string alias = rt.Properties[propIndex++].Name; ColumnFragment functionCol = new ColumnFragment(null, null); functionCol.Literal = HandleFunction(fa, a.Arguments[0].Accept(this)); functionCol.ColumnAlias = alias; select.Columns.Add(functionCol); } return(select); }
protected internal SelectStatement FuseSelectWithInnerSelect(SelectStatement outer, SelectStatement inner) { string oldTableName = (inner.From as TableFragment).Name; string newTableName = inner.Name; Dictionary <string, ColumnFragment> dicColumns = new Dictionary <string, ColumnFragment>(); foreach (ColumnFragment cf in inner.Columns) { if (cf.ColumnAlias != null) { dicColumns.Add(cf.ColumnAlias, cf); } } outer.From = inner.From; (outer.From as TableFragment).Name = newTableName; // Dispatch Where if (outer.Where == null) { outer.Where = inner.Where; } else if (inner.Where != null) { outer.Where = new BinaryFragment() { Left = outer.Where, Right = inner.Where, Operator = "AND" }; } VisitAndReplaceTableName(outer.Where, oldTableName, newTableName, dicColumns); // For the next constructions, either is defined on outer or at inner, not both // Dispatch Limit if (outer.Limit == null) { outer.Limit = inner.Limit; VisitAndReplaceTableName(outer.Limit, oldTableName, newTableName, dicColumns); } // Dispatch GroupBy if (outer.GroupBy == null && inner.GroupBy != null) { foreach (SqlFragment sf in inner.GroupBy) { outer.AddGroupBy(sf); } foreach (SqlFragment sf in outer.GroupBy) { VisitAndReplaceTableName(sf, oldTableName, newTableName, dicColumns); } } // Dispatch OrderBy if (outer.OrderBy != null || inner.OrderBy != null) { if (inner.OrderBy != null) { foreach (SortFragment sf in inner.OrderBy) { outer.AddOrderBy(sf); } } foreach (SortFragment sf in outer.OrderBy) { VisitAndReplaceTableName(sf, oldTableName, newTableName, dicColumns); } } // Dispatch Skip if (outer.Skip == null) { outer.Skip = inner.Skip; } return(outer); }
/// <summary> /// If input sqlFragment is a group by structure, is flattened to remove some nested correlation queries. /// </summary> /// <param name="input"></param> /// <returns></returns> private SqlFragment TryFlatteningGroupBy(SqlFragment input) { SelectStatement select = null; SelectStatement tmpFrag = null, tmpFrag2 = null, tmpFrag3 = null; InputFragment table = null; string objName = null, colName = null, queryName = null; // First part assert is a kind-of structure we are looking for tmpFrag = input as SelectStatement; if (tmpFrag == null) { goto NoChanges; } tmpFrag = (tmpFrag).From as SelectStatement; if (tmpFrag == null) { goto NoChanges; } queryName = tmpFrag.Name; if (tmpFrag.Columns.Count < 2) { goto NoChanges; } if (!(tmpFrag.Columns[0] is ColumnFragment)) { goto NoChanges; } colName = objName = (tmpFrag.Columns[0] as ColumnFragment).ActualColumnName; tmpFrag2 = tmpFrag.From as SelectStatement; if (tmpFrag2 == null) { goto NoChanges; } if (tmpFrag2.Columns.Count < 1 || !(tmpFrag2.Columns[0] is ColumnFragment)) { goto NoChanges; } if (string.CompareOrdinal(objName, tmpFrag2.Columns[0].ActualColumnName) != 0) { goto NoChanges; } if (tmpFrag.Columns[1].Literal == null) { goto NoChanges; } tmpFrag2 = tmpFrag.Columns[1].Literal as SelectStatement; if (tmpFrag2 == null) { goto NoChanges; } if ((tmpFrag2.Columns.Count != 1) || !(tmpFrag2.Columns[0].Literal != null)) { goto NoChanges; } tmpFrag3 = tmpFrag2.From as SelectStatement; if (tmpFrag3 == null) { goto NoChanges; } table = tmpFrag3.From as InputFragment; if (table == null) { goto NoChanges; } FunctionFragment func = tmpFrag2.Columns[0].Literal as FunctionFragment; if (tmpFrag3.Columns.Count != 1 || !(tmpFrag3.Columns[0] is ColumnFragment)) { goto NoChanges; } if (func == null) { goto NoChanges; } // Yes it is the kind-of type we like, then optimize it select = new SelectStatement(this); table.Name = null; string tableName = null; TableFragment t = tmpFrag3.From as TableFragment; if (t == null) { tableName = tmpFrag3.Columns[0].TableName; } else { tableName = t.Table; } select.From = table; select.Columns.Add(new ColumnFragment(tableName, colName)); select.Columns.Add(new ColumnFragment(tableName, "C0") { ColumnAlias = "C1", Literal = new FunctionFragment() { Argument = new ColumnFragment(tableName, tmpFrag3.Columns[0].ActualColumnName), Distinct = tmpFrag3.IsDistinct, Name = func.Name } }); select.Wrap(null); select.Name = queryName; select.AddGroupBy(select.Columns[0]); (input as SelectStatement).From = select; NoChanges: return(input); }