예제 #1
0
        private IDataExecutionPlanNode FoldToStreamAggregate(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints)
        {
            // Use stream aggregate where possible - if there are no grouping fields or the groups can be folded into sorts
            var streamAggregate = new StreamAggregateNode {
                Source = Source
            };

            streamAggregate.GroupBy.AddRange(GroupBy);

            foreach (var aggregate in Aggregates)
            {
                streamAggregate.Aggregates[aggregate.Key] = aggregate.Value;
            }

            if (!IsScalarAggregate)
            {
                // Use hash grouping if explicitly requested with optimizer hint
                if (hints != null && hints.Any(h => h.HintKind == OptimizerHintKind.HashGroup))
                {
                    return(this);
                }

                var sorts = new SortNode {
                    Source = Source
                };

                foreach (var group in GroupBy)
                {
                    sorts.Sorts.Add(new ExpressionWithSortOrder {
                        Expression = group, SortOrder = SortOrder.Ascending
                    });
                }

                streamAggregate.Source = sorts.FoldQuery(dataSources, options, parameterTypes, hints);

                // Don't bother using a sort + stream aggregate if none of the sorts can be folded
                if (streamAggregate.Source == sorts && sorts.PresortedCount == 0)
                {
                    return(this);
                }
            }

            return(streamAggregate);
        }
예제 #2
0
        public override IDataExecutionPlanNode FoldQuery(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints)
        {
            Source        = Source.FoldQuery(dataSources, options, parameterTypes, hints);
            Source.Parent = this;

            // Remove any duplicated column names
            for (var i = Columns.Count - 1; i >= 0; i--)
            {
                if (Columns.IndexOf(Columns[i]) < i)
                {
                    Columns.RemoveAt(i);
                }
            }

            // If one of the fields to include in the DISTINCT calculation is the primary key, there is no possibility of duplicate
            // rows so we can discard the distinct node
            var schema = Source.GetSchema(dataSources, parameterTypes);

            if (!String.IsNullOrEmpty(schema.PrimaryKey) && Columns.Contains(schema.PrimaryKey, StringComparer.OrdinalIgnoreCase))
            {
                return(Source);
            }

            if (Source is FetchXmlScan fetch)
            {
                fetch.FetchXml.distinct          = true;
                fetch.FetchXml.distinctSpecified = true;

                // Ensure there is a sort order applied to avoid paging issues
                if (fetch.Entity.Items == null || !fetch.Entity.Items.OfType <FetchOrderType>().Any())
                {
                    // Sort by each attribute. Make sure we only add one sort per attribute, taking virtual attributes
                    // into account (i.e. don't attempt to sort on statecode and statecodename)
                    var sortedAttributes = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                    foreach (var column in Columns)
                    {
                        if (!schema.ContainsColumn(column, out var normalized))
                        {
                            continue;
                        }

                        if (!sortedAttributes.Add(normalized))
                        {
                            continue;
                        }

                        var parts = normalized.Split('.');
                        if (parts.Length != 2)
                        {
                            continue;
                        }

                        if (parts[0].Equals(fetch.Alias, StringComparison.OrdinalIgnoreCase))
                        {
                            var attr = dataSources[fetch.DataSource].Metadata[fetch.Entity.name].Attributes.SingleOrDefault(a => a.LogicalName.Equals(parts[1], StringComparison.OrdinalIgnoreCase));

                            if (attr == null)
                            {
                                continue;
                            }

                            if (attr.AttributeOf != null && !sortedAttributes.Add(parts[0] + "." + attr.AttributeOf))
                            {
                                continue;
                            }

                            fetch.Entity.AddItem(new FetchOrderType {
                                attribute = parts[1]
                            });
                        }
                        else
                        {
                            var linkEntity = fetch.Entity.FindLinkEntity(parts[0]);
                            var attr       = dataSources[fetch.DataSource].Metadata[linkEntity.name].Attributes.SingleOrDefault(a => a.LogicalName.Equals(parts[1], StringComparison.OrdinalIgnoreCase));

                            if (attr == null)
                            {
                                continue;
                            }

                            if (attr.AttributeOf != null && !sortedAttributes.Add(parts[0] + "." + attr.AttributeOf))
                            {
                                continue;
                            }

                            linkEntity.AddItem(new FetchOrderType {
                                attribute = parts[1]
                            });
                        }
                    }
                }

                return(fetch);
            }

            // If the data is already sorted by all the distinct columns we can use a stream aggregate instead.
            // We don't mind what order the columns are sorted in though, so long as the distinct columns form a
            // prefix of the sort order.
            var requiredSorts = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var col in Columns)
            {
                if (!schema.ContainsColumn(col, out var column))
                {
                    return(this);
                }

                requiredSorts.Add(column);
            }

            if (!schema.IsSortedBy(requiredSorts))
            {
                return(this);
            }

            var aggregate = new StreamAggregateNode {
                Source = Source
            };

            Source.Parent = aggregate;

            for (var i = 0; i < requiredSorts.Count; i++)
            {
                aggregate.GroupBy.Add(schema.SortOrder[i].ToColumnReference());
            }

            return(aggregate);
        }