Architettura e progettazioneArchitecture and Design

Il modulo di generazione SQL nel Provider di esempio viene implementato come un visitatore dell'albero delle espressioni che rappresenta l'albero dei comandi.The SQL generation module in the Sample Provider is implemented as a visitor on the expression tree that represents the command tree. La generazione viene eseguita in un unico passaggio sull'albero delle espressioni.The generation is done in a single pass over the expression tree.

I nodi dell'albero vengono elaborati dal basso verso l'alto.The nodes of the tree are processed from the bottom up. Prima viene prodotta una struttura intermedia: SqlSelectStatement o SqlBuilder. Entrambe implementano ISqlFragment.First, an intermediate structure is produced: SqlSelectStatement or SqlBuilder, both implementing ISqlFragment. Successivamente da tale struttura viene prodotta l'istruzione SQL della stringa.Next, the string SQL statement is produced from that structure. Esistono due motivi per cui viene prodotta la struttura intermedia:There are two reasons for the intermediate structure:

  • Un'istruzione SQL SELECT viene popolata in modo non corretto da un punto di vista logico.Logically, a SQL SELECT statement is populated out of order. I nodi che partecipano alla clausola FROM vengono visitati prima di quelli che partecipano alla clausola WHERE, GROUP BY e ORDER BY.The nodes that participate in the FROM clause are visited before the nodes that participate in the WHERE, GROUP BY, and the ORDER BY clause.

  • Per evitare conflitti durante la ridenominazione degli alias, è necessario identificare tutti gli alias usati.To rename aliases, you must identify all used aliases to avoid collisions during renaming. È possibile rinviare le scelte di ridenominazione in SqlBuilder, usando gli oggetti Symbol per rappresentare le colonne candidate per la ridenominazione.To defer the renaming choices in SqlBuilder, use Symbol objects to represent the columns that are candidates for renaming.

DiagrammaDiagram

Nella prima fase, durante la visita dell'albero delle espressioni, le espressioni vengono raggruppate in oggetti SqlSelectStatements e i join e gli alias di join vengono resi bidimensionali.In the first phase, while visiting the expression tree, expressions are grouped into SqlSelectStatements, joins are flattened, and join aliases are flattened. Durante questo passaggio, gli oggetti Symbol rappresentano colonne o alias di input che possono essere rinominati.During this pass, Symbol objects represent columns or input aliases that may be renamed.

Nella seconda fase, durante la produzione della stringa effettiva, gli alias vengono rinominati.In the second phase, while producing the actual string, aliases are renamed.

Strutture di datiData Structures

Questa sezione vengono illustrati i tipi utilizzati nel Provider di esempio consentono di compilare un'istruzione SQL.This section discusses the types used in the Sample Provider that you use to build a SQL statement.

ISqlFragmentISqlFragment

Questa sezione analizza le classi che implementano l'interfaccia ISqlFragment che ha una duplice funzione:This section covers the classes that implement the ISqlFragment interface, which serves two purposes:

  • Un tipo restituito comune per tutti i metodi del visitatore.A common return type for all the visitor methods.

  • Fornisce un metodo per scrivere la stringa SQL finale.Gives a method to write the final SQL string.

internal interface ISqlFragment {  
   void WriteSql(SqlWriter writer, SqlGenerator sqlGenerator);  
}  

SqlBuilderSqlBuilder

SqlBuilder è un dispositivo di raccolta per la stringa SQL finale, simile a StringBuilder.SqlBuilder is a gathering device for the final SQL string, similar to StringBuilder. È formato dalle stringhe che costituiscono l'SQL finale, insieme all'oggetto ISqlFragments che può essere convertito in stringhe.It consists of the strings that make up the final SQL, along with ISqlFragments that can be converted into strings.

internal sealed class SqlBuilder : ISqlFragment {  
   public void Append(object s)  
   public void AppendLine()  
   public bool IsEmpty  
}  

SqlSelectStatementSqlSelectStatement

SqlSelectStatement rappresenta un'istruzione SQL SELECT canonica della forma "SELECT...SqlSelectStatement represents a canonical SQL SELECT statement of the shape "SELECT … DA..FROM .. DOVE...WHERE … RAGGRUPPA PER...GROUP BY … ORDER BY".ORDER BY".

Ognuna delle clausole SQL viene rappresentata da un oggetto StringBuilderEach of the SQL clauses is represented by a StringBuilder. e inoltre rileva se è stato specificato Distinct e se l'istruzione è al livello più alto.In addition, it tracks whether Distinct has been specified and whether the statement is topmost. Se l'istruzione non è al livello più alto, la clausola ORDER BY viene omessa, a meno che nell'istruzione non sia inclusa anche una clausola TOP.If the statement is not topmost, the ORDER BY clause is omitted unless the statement also has a TOP clause.

FromExtents contiene l'elenco di input per l'istruzione SELECT.FromExtents contains the list of inputs for the SELECT statement. Generalmente è presente un solo elemento.There is usually just one element in this. È possibile che le istruzioni SELECT per i join contengano temporaneamente più di un elemento.SELECT statements for joins may temporarily have more than one element.

Se l'istruzione SELECT viene creata da un nodo di join, SqlSelectStatement gestisce un elenco di tutti gli extent che sono stati resi bidimensionali nel join in AllJoinExtents.If the SELECT statement is created by a Join node, SqlSelectStatement maintains a list of all the extents that have been flattened in the join in AllJoinExtents. OuterExtents rappresenta i riferimenti esterni di SqlSelectStatement e viene usato per la ridenominazione degli alias di input.OuterExtents represents outer references of the SqlSelectStatement and is used for input alias renaming.

internal sealed class SqlSelectStatement : ISqlFragment {  
   internal bool IsDistinct { get, set };  
   internal bool IsTopMost  

   internal List<Symbol> AllJoinExtents { get, set };  
   internal List<Symbol> FromExtents { get};  
   internal Dictionary<Symbol, bool> OuterExtents { get};  

   internal TopClause Top { get, set };  

   internal SqlBuilder Select {get};  
   internal SqlBuilder From  
   internal SqlBuilder Where  
   internal SqlBuilder GroupBy  
   public SqlBuilder OrderBy  
}  

TopClauseTopClause

TopClause rappresenta l'espressione TOP in un oggetto SqlSelectStatement.TopClause represents the TOP expression in a SqlSelectStatement. La proprietà TopCount indica il numero di righe TOP che devono essere selezionate.The TopCount property indicates how many TOP rows should be selected. Se WithTies è true, TopClause è stato compilato da un oggetto DbLimitExpession.When WithTies is true, the TopClause was built from a DbLimitExpession.

class TopClause : ISqlFragment {  
   internal bool WithTies {get}  
   internal ISqlFragment TopCount {get}  
   internal TopClause(ISqlFragment topCount, bool withTies)  
   internal TopClause(int topCount, bool withTies)  
}  

SymbolsSymbols

Le classi correlate ai simboli e la tabella dei simboli eseguono la ridenominazione degli alias di input, la bidimensionalità degli alias di join e la ridenominazione degli alias di colonna.The Symbol-related classes and the symbol table perform input alias renaming, join alias flattening, and column alias renaming.

La classe Symbol rappresenta un extent, un'istruzione SELECT annidata o una colonna.The Symbol class represents an extent, a nested SELECT statement, or a column. Viene usata in sostituzione di un alias effettivo per consentire che venga rinominato dopo essere stato usato e contiene inoltre informazioni aggiuntive relative all'elemento che rappresenta (come il tipo).It is used instead of an actual alias to allow for renaming after it has been used and it also carries additional information for the artifact it represents (like the type).

class Symbol : ISqlFragment {  
   internal Dictionary<string, Symbol> Columns {get}  
   internal bool NeedsRenaming {get, set}  
   internal bool IsUnnest {get, set}   //not used  

   public string Name{get}  
   public string NewName {get,set}  
   internal TypeUsage Type {get, set}  

   public Symbol(string name, TypeUsage type)  
}  

Name archivia l'alias originale per l'extent rappresentato, l'istruzione SELECT annidata o una colonna.Name stores the original alias for the represented extent, nested SELECT statement, or a column.

NewName archivia l'alias che verrà usato nell'istruzione SQL SELECT.NewName stores the alias that will be used in the SQL SELECT statement. Viene originariamente impostato su Name e rinominato solo se necessario quando viene generata la query della stringa finale.It is originally set to Name, and only renamed if needed when generating the final string query.

Type è utile solo per i simboli che rappresentano extent e istruzioni SELECT annidate.Type is only useful for symbols representing extents and nested SELECT statements.

SymbolPairSymbolPair

La classe SymbolPair viene usata per rendere bidimensionali i record.The SymbolPair class addresses record flattening.

Si consideri un'espressione di proprietà D (v, "j3.j2.j1.a.x") in cui v è un VarRef v, j1, j2 j3 sono join, a è un extent e x è una colonna.Consider a property expression D(v, "j3.j2.j1.a.x") where v is a VarRef, j1, j2, j3 are joins, a is an extent, and x is a columns.

Tale espressione dovrà essere convertita in {j'}.{x'}.This has to be translated eventually into {j'}.{x'}. Il campo di origine rappresenta l'oggetto SqlStatement più esterno che rappresenta un'espressione di join, ad esempio j2. Si tratta sempre di un simbolo Join.The source field represents the outermost SqlStatement, representing a join expression (say j2); this is always a Join symbol. Il campo di colonna si sposta da un simbolo di join al successivo, fino ad arrestarsi in corrispondenza di un simbolo non di join.The column field moves from one join symbol to the next until it stops at a non-join symbol. Tale simbolo viene restituito quando viene visitato un oggetto DbPropertyExpression ma non viene mai aggiunto a un oggetto SqlBuilder.This is returned when visiting a DbPropertyExpression but is never added to a SqlBuilder.

class SymbolPair : ISqlFragment {  
   public Symbol Source;  
   public Symbol Column;  
   public SymbolPair(Symbol source, Symbol column)  
}  

JoinSymbolJoinSymbol

Un simbolo Join è un simbolo che rappresenta un'istruzione SELECT annidata con un join o un input di join.A Join symbol is a Symbol that represents a nested SELECT statement with a join or a join input.

internal sealed class JoinSymbol : Symbol {  
   internal List<Symbol> ColumnList {get, set}  
   internal List<Symbol> ExtentList {get}  
   internal List<Symbol> FlattenedExtentList {get, set}  
   internal Dictionary<string, Symbol> NameToExtent {get}  
   internal bool IsNestedJoin {get, set}  

   public JoinSymbol(string name, TypeUsage type, List<Symbol> extents)  
}  

ColumnList rappresenta l'elenco di colonne della clausola SELECT quando questo simbolo rappresenta un'istruzione SQL SELECT.ColumnList represents the list of columns in the SELECT clause if this symbol represents a SQL SELECT statement. ExtentList è l'elenco degli extent inclusi nella clausola SELECT.ExtentList is the list of extents in the SELECT clause. Se il join presenta più extent resi bidimensionali al livello superiore, FlattenedExtentList rileva gli extent per assicurarsi che i rispettivi alias vengano rinominati correttamente.If the join has multiple extents flattened at the top level, FlattenedExtentList tracks the extents to ensure that extent aliases are renamed correctly.

NameToExtent include in ExtentList tutti gli extent di un dizionario.NameToExtent has all the extents in ExtentList as a dictionary. IsNestedJoin viene usato per determinare se un oggetto JoinSymbol è un normale simbolo di join oppure un simbolo con un oggetto SqlSelectStatement corrispondente.IsNestedJoin is used to determine whether a JoinSymbol is an ordinary join symbol or one that has a corresponding SqlSelectStatement.

Tutti gli elenchi vengono impostati esattamente una volta e quindi usati per ricerche o enumerazione.All the lists are set exactly once and then used for lookups or enumeration.

SymbolTableSymbolTable

SymbolTable viene usato per risolvere i nomi di variabile in simboli.SymbolTable is used to resolve variable names to Symbols. SymbolTable viene implementato come stack con una nuova voce per ogni ambito.SymbolTable is implemented as a stack with a new entry for each scope. Le ricerche vengono eseguite dalla parte superiore alla parte inferiore dello stack, fino a che non viene trovata una voce.Lookups search from the top of the stack to the bottom until an entry is found.

internal sealed class SymbolTable {  
   internal void EnterScope()  
   internal void ExitScope()  
   internal void Add(string name, Symbol value)  
   internal Symbol Lookup(string name)  
}  

Esiste un solo oggetto SymbolTable per un'istanza del modulo di generazione SQL.There is only one SymbolTable per one instance of the Sql Generation module. Per ogni nodo relazionale vengono immessi e terminati ambiti.Scopes are entered and exited for each relational node. Tutti i simboli inclusi nei primi ambiti sono visibili agli ambiti successivi, a meno che non siano nascosti dagli altri simboli con lo stesso nome.All symbols in earlier scopes are visible to later scopes unless hidden by other symbols with the same name.

Stato globale per il visitatoreGlobal State for the Visitor

Come supporto nelle operazioni di ridenominazione di alias e colonne, è utile tenere un elenco di tutti i nomi di colonna (AllColumnNames) e degli alias degli extent (AllExtentNames) usati nel primo passaggio sull'albero della query.To assist in renaming of aliases and columns, maintain a list of all the column names (AllColumnNames) and extent aliases (AllExtentNames) that have been used in the first pass over the query tree. La tabella dei simboli consente di risolvere i nomi di variabile in simboli.The symbol table resolves variable names to Symbols. IsVarRefSingle viene usato solo a scopo di verifica e non è strettamente necessario.IsVarRefSingle is only used for verification purposes, it is not strictly necessary.

I due stack usati tramite CurrentSelectStatement e IsParentAJoin vengono usati per passare i "parametri" dai nodi padre ai nodi figlio, dal momento che il modello di visitatore non consente di passare i parametri.The two stacks used via CurrentSelectStatement and IsParentAJoin are used to pass "parameters" from parent to child nodes, since the visitor pattern does not allow us to pass parameters.

internal Dictionary<string, int> AllExtentNames {get}  
internal Dictionary<string, int> AllColumnNames {get}  
SymbolTable symbolTable = new SymbolTable();  
bool isVarRefSingle = false;  

Stack<SqlSelectStatement> selectStatementStack;  
private SqlSelectStatement CurrentSelectStatement{get}  

Stack<bool> isParentAJoinStack;  
private bool IsParentAJoin{get}  

Scenari comuniCommon Scenarios

Questa sezione illustra gli scenari comuni del provider.This section discusses common provider scenarios.

Raggruppamento di nodi di espressione in Istruzioni SQLGrouping Expression Nodes into SQL Statements

Quando si incontra il primo nodo relazionale (in genere un extent DbScanExpression) durante la visita dell'albero dal basso verso l'alto, viene creato un oggetto SqlSelectStatement.A SqlSelectStatement is created when the first relational node is encountered (typically a DbScanExpression extent) when visiting the tree from the bottom up. Per produrre un'istruzione SQL SELECT con il minor numero possibile di query annidate, aggregare in tale oggetto SqlSelectStatement il maggior numero possibile di nodi padre.To produce a SQL SELECT statement with as few nested queries as possible, aggregate as many of its parent nodes as possible in that SqlSelectStatement.

La decisione relativa alla possibilità di aggiungere un nodo specificato (relazionale) all'oggetto SqlSelectStatement corrente (quello restituito durante la visita dell'input) o alla necessità di avviare una nuova istruzione viene elaborata dal metodo IsCompatible e dipende dagli elementi già inclusi in SqlSelectStatement, ovvero dai nodi che si trovano al di sotto del nodo specificato.The decision of whether a given (relational) node can be added to the current SqlSelectStatement (the one returned when visiting the input) or if a new statement needs to be started is computed by the method IsCompatible and depends on what is already in the SqlSelectStatement, which depends on what nodes were below the given node.

In genere, se le clausole dell'istruzione SQL vengono valutate dopo le clausole in cui i nodi considerati per l'unione non sono vuoti, non è possibile aggiungere il nodo all'istruzione corrente.Typically, if SQL statement clauses are evaluated after clauses where the nodes being considered for merging are not empty, the node cannot be added to the current statement. Se ad esempio il nodo successivo è un filtro, tale nodo può essere incorporato nell'oggetto SqlSelectStatement corrente solo se si verificano le condizioni seguenti:For example, if the next node is a Filter, that node can be incorporated into the current SqlSelectStatement only if the following is true:

  • L'elenco SELECT è vuoto.The SELECT list is empty. Se l'elenco SELECT non è vuoto, l'elenco di selezione è stato prodotto da un nodo che precede il filtro e il predicato può fare riferimento alle colonne prodotte dall'elenco SELECT.If the SELECT list is not empty, the select list was produced by a node preceding the filter and the predicate may refer to columns produced by that SELECT list.

  • La classe GROUPBY è vuota.The GROUPBY is empty. Se GROUPBY non è vuota, l'aggiunta del filtro comporterebbe l'applicazione di filtri prima del raggruppamento e tale procedura non è corretta.If the GROUPBY is not empty, adding the filter would mean filtering before grouping, which is not correct.

  • La clausola TOP è vuota.The TOP clause is empty. Se la clausola TOP non è vuota, l'aggiunta del filtro comporterebbe l'applicazione di filtri prima dell'esecuzione di TOP e tale procedura non è corretta.If the TOP clause is not empty, adding the filter would mean filtering before doing TOP, which is not correct.

Queste indicazioni non valgono per i nodi non relazionali come DbConstantExpression o le espressioni aritmetiche, poiché questi sono sempre inclusi come parte di un oggetto SqlSelectStatement esistente.This does not apply to non-relational nodes like DbConstantExpression or arithmetic expressions, because these are always included as part of an existing SqlSelectStatement.

Quando inoltre si incontra la radice dell'albero dei join (un nodo join privo di join padre), viene avviato un nuovo oggetto SqlSelectStatement.Also, when encountering the root of join tree (a join node that does not have a join parent), a new SqlSelectStatement is started. Tutti i rispettivi join figlio del lato sinistro vengono aggregati nell'oggetto SqlSelectStatement.All of its left spine join children are aggregated into that SqlSelectStatement.

Ogni qualvolta viene avviato un nuovo oggetto SqlSelectStatement e quello corrente viene aggiunto all'input, può essere necessario completare l'oggetto SqlSelectStatement corrente aggiungendo colonne di proiezione (una clausola SELECT), se non ne esiste già una.Whenever a new SqlSelectStatement is started, and the current one is added to the input, the current SqlSelectStatement may need to be completed by adding projection columns (a SELECT clause) if one does not exist. È possibile eseguire questa operazione con il metodo AddDefaultColumns che analizza FromExtents di SqlSelectStatement e aggiunge all'elenco delle colonne previste tutte le colonne che rientrano nell'ambito dell'elenco di extent rappresentato da FromExtents.This is done with the method AddDefaultColumns, which looks at the FromExtents of the SqlSelectStatement and adds all the columns that the list of extents represented by FromExtents brings in scope to the list of projected columns. Ciò accade in quanto a questo punto non è noto a quali colonne fanno riferimento gli altri nodi.This is done, because at that point, it is unknown which columns are referenced by the other nodes. È possibile ottimizzare la procedura includendo nella proiezione le sole colonne che possono essere usate in un momento successivo.This can be optimized to only project the columns that can later be used.

Bidimensionalità del joinJoin Flattening

La proprietà IsParentAJoin consente di stabilire se è possibile rendere bidimensionale un join specificato.The IsParentAJoin property helps determine whether a given join can be flattened. In particolare, IsParentAJoin restituisce true solo per il figlio sinistro di un join e per ogni oggetto DbScanExpression che costituisce un input immediato per un join. In questo caso, il nodo figlio riusa lo stesso oggetto SqlSelectStatement che verrà successivamente usato dal padre.In particular, IsParentAJoin returns true only for the left child of a join and for each DbScanExpression that is an immediate input to a join, in which case that child node reuses the same SqlSelectStatement that the parent would later use. Per altre informazioni, vedere "Unione di espressioni".For more information, see "Join Expressions".

Reindirizzamento degli alias di inputInput Alias Redirecting

È possibile ottenere il reindirizzamento degli alias di input usando la tabella dei simboli.Input alias redirecting is accomplished with the symbol table.

Per illustrare reindirizzamento degli alias di input, fare riferimento al primo esempio in generazione SQL dagli alberi dei comandi - consigliate.To explain input alias redirecting, refer to the first example in Generating SQL from Command Trees - Best Practices. In questo esempio "a" deve essere reindirizzato in "b" nella proiezione.There "a" needed to be redirected to "b" in the projection.

Quando viene creato un oggetto SqlSelectStatement, l'extent che costituisce l'input per il nodo viene inserito nella proprietà From dell'oggetto SqlSelectStatement.When a SqlSelectStatement object is created, the extent that is the input to the node is put in the From property of the SqlSelectStatement. Per rappresentare tale extent, viene creato un oggetto Symbol (<simbolo_b>) in base al nome dell'associazione di input ("b") e "AS " + <simbolo_b> viene aggiunto alla clausola FROM.A Symbol (<symbol_b>) is created based on the input binding name ("b") to represent that extent and "AS " + <symbol_b> is appended to the From Clause. Il simbolo viene inoltre aggiunto alla proprietà FromExtents.The symbol is also added to the FromExtents property.

Il simbolo viene aggiunto anche alla tabella dei simboli per consentire il collegamento al nome dell'associazione di input ("b", <symbol_b>).The symbol is also added to the symbol table to link the input binding name to it ("b", <symbol_b>).

Se un nodo successivo riusa lo stesso oggetto SqlSelectStatement, viene aggiunta una voce alla tabella dei simboli per collegare il rispettivo nome dell'associazione di input al simbolo.If a subsequent node reuses that SqlSelectStatement, it adds an entry to the symbol table to link its input binding name to that symbol. Nel nostro esempio DbProjectExpression con il nome di associazione di input di "a" riutilizzerebbe SqlSelectStatement e aggiungere ("a", < symbol_b >) alla tabella.In our example, the DbProjectExpression with the input binding name of "a" would reuse the SqlSelectStatement and add ("a", < symbol_b>) to the table.

Quando le espressioni fanno riferimento al nome dell'associazione di input del nodo che sta riusando l'oggetto SqlSelectStatement, tale riferimento viene risolto usando la tabella dei simboli nel simbolo reindirizzato corretto.When expressions reference the input binding name of the node that is reusing the SqlSelectStatement, that reference is resolved using the symbol table to the correct redirected symbol. Quando "a" da "a.x" viene risolto durante la visita dell'oggetto DbVariableReferenceExpression che rappresenta "a", verrà risolto nell'oggetto Symbol <symbol_b>.When "a" from "a.x" is resolved while visiting the DbVariableReferenceExpression representing "a" it will resolve to the Symbol <symbol_b>.

Bidimensionalità degli alias di joinJoin Alias Flattening

Il bidimensionalità degli alias di join viene realizzata durante la visita di un oggetto DbPropertyExpression, come descritto nella sezione intitolata DbPropertyExpression.Join alias flattening is achieved when visiting a DbPropertyExpression as described in the section titled DbPropertyExpression.

Ridenominazione dei nomi di colonna e degli alias degli extentColumn Name and Extent Alias Renaming

È possibile risolvere il problema della ridenominazione dei nomi di colonna e degli alias degli extent mediante l'uso di simboli che vengono semplicemente sostituiti dagli alias nella seconda fase della generazione, descritta nella sezione intitolata Seconda fase della generazione SQL: generazione della stringa di comando.The issue of column name and extent alias renaming is addressed by using symbols that only get substituted with aliases in the second phase of the generation described in the section titled Second Phase of SQL Generation: Generating the String Command.

Prima fase della generazione SQL: visita dell'albero delle espressioniFirst Phase of the SQL Generation: Visiting the Expression Tree

Questa sezione descrive la prima fase di generazione SQL, quando viene visitata l'espressione che rappresenta la query e viene prodotta una struttura intermedia, ovvero un oggetto SqlSelectStatement o un oggetto SqlBuilder.This section describes the first phase of SQL generation, when the expression representing the query is visited and an intermediate structure is produced, either a SqlSelectStatement or a SqlBuilder.

Questa sezione descrive i principi su cui si basa la visita di diverse categorie del nodo di espressione e vengono forniti dettagli relativi alla visita di tipi di espressione specifici.This section describes the principles of visiting different expression node categories, and details of visiting specific expression types.

Nodi relazionali (non join)Relational (Non-Join) Nodes

Di seguito vengono indicati i tipi di espressione che supportano nodi non join:The following expression types support non-join nodes:

  • DbDistinctExpressionDbDistinctExpression

  • DbFilterExpressionDbFilterExpression

  • DbGroupByExpressionDbGroupByExpression

  • DbLimitExpessionDbLimitExpession

  • DbProjectExpressionDbProjectExpression

  • DbSkipExpressionDbSkipExpression

  • DbSortExpressionDbSortExpression

Il modello che viene seguito per la visita di questi nodi è il seguente:Visiting these nodes follows the following pattern:

  1. Visitare l'input relazionale e ottenere l'oggetto SqlSelectStatement risultante.Visit the relational input and get the resulting SqlSelectStatement. L'input in un nodo relazionale potrebbe essere uno degli elementi seguenti:The input to a relational node could be one of the following:

    • Un nodo relazionale che include un extent, ad esempio un oggetto DbScanExpression.A relational node, including an extent (a DbScanExpression, for example). Quando si visita un nodo di questo tipo, viene restituito un oggetto SqlSelectStatement.Visiting such a node returns a SqlSelectStatement.

    • Un'espressione dell'operazione di impostazione, ad esempio UNION ALL.A set operation expression (UNION ALL, for example). Il risultato deve essere racchiuso tra parentesi e inserito nella clausola FROM di un nuovo oggetto SqlSelectStatement.The result has to be wrapped in brackets and put in the FROM clause of a new SqlSelectStatement.

  2. Controllare se il nodo corrente può essere aggiunto all'oggetto SqlSelectStatement prodotto dall'input.Check whether the current node can be added to the SqlSelectStatement produced by the input. Questa procedura viene descritta nella sezione intitolata Raggruppamento di espressioni nelle istruzioni SQL.The section titled Grouping Expressions into SQL Statements describes this. Se non può essere aggiunto,If not,

    • Visualizzare l'oggetto SqlSelectStatement corrente.Pop the current SqlSelectStatement object.

    • Creare un nuovo oggetto SqlSelectStatement e aggiungere l'oggetto SqlSelectStatement visualizzato come FROM del nuovo oggetto SqlSelectStatement.Create a new SqlSelectStatement object and add the popped SqlSelectStatement as the FROM of the new SqlSelectStatement object.

    • Inserire il nuovo oggetto in cima allo stack.Put the new object on top of the stack.

  3. Reindirizzare l'associazione di espressioni di input nel simbolo corretto dall'input.Redirect the input expression binding to the correct symbol from the input. Queste informazioni si trovano nell'oggetto SqlSelectStatement.This information is maintained in the SqlSelectStatement object.

  4. Aggiungere un nuovo ambito SymbolTable.Add a new SymbolTable scope.

  5. Visitare la parte non di input dell'espressione, ad esempio Projection e Predicate.Visit the non-input part of the expression (for example, Projection and Predicate).

  6. Visualizzare tutti gli oggetti aggiunti agli stack globali.Pop all the objects added to the global stacks.

Non esiste un diretto equivalente di DbSkipExpression in SQL.DbSkipExpression not have a direct equivalent in SQL. Logicamente viene convertito in:Logically, it is translated into:

SELECT Y.x1, Y.x2, ..., Y.xn  
FROM (  
   SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number]   
   FROM input as X   
   ) as Y  
WHERE Y.[row_number] > count   
ORDER BY sk1, sk2, ...  

Espressioni di joinJoin Expressions

Gli elementi seguenti sono considerati espressioni di join e vengono elaborati come di consueto, ovvero mediante il metodo VisitJoinExpression:The following are considered join expressions and they are processed in a common way, by the VisitJoinExpression method:

  • DbApplyExpressionDbApplyExpression

  • DbJoinExpressionDbJoinExpression

  • DbCrossJoinExpressionDbCrossJoinExpression

I passaggi della visita sono i seguenti:The following are the visit steps:

Prima di visitare i figli, viene chiamato il metodo IsParentAJoin per controllare se il nodo di join è un figlio di un join su un lato sinistro.First, before visiting the children, IsParentAJoin is invoked to check whether the join node is a child of a join along a left spine. Se viene restituito False, viene avviato un nuovo oggetto SqlSelectStatement.If it returns false, a new SqlSelectStatement is started. In questo caso, i join vengono visitati in modo diverso dal resto dei nodi, in quanto il padre (il nodo di join) crea l'oggetto SqlSelectStatement per consentirne l'uso da parte dei figli.In that sense, joins are visited differently from the rest of the nodes, as the parent (the join node) creates the SqlSelectStatement for the children to possibly use.

Elaborare quindi gli input uno alla volta.Second, process the inputs one at a time. Per ogni input:For each input:

  1. Visitare l'input.Visit the input.

  2. Completare l'elaborazione del risultato della visita dell'input chiamando il metodo ProcessJoinInputResult, responsabile della gestione della tabella dei simboli, dopo aver visitato un figlio di un'espressione di join e possibilmente aver completato l'oggetto SqlSelectStatement prodotto dal figlio.Post process the result of visiting the input by invoking ProcessJoinInputResult, which is responsible for maintaining the symbol table after visiting a child of a join expression and possibly finishing the SqlSelectStatement produced by the child. Il risultato del figlio potrebbe essere uno dei seguenti:The child's result could be one of the following:

    • Un oggetto SqlSelectStatement diverso da quello al quale verrà aggiunto il padre.A SqlSelectStatement different from the one to which the parent will be added. In questo caso, può essere necessario completarlo aggiungendo colonne predefinite.In such case, it may need to be completed by adding default columns. Se l'input è un join, è necessario creare un nuovo simbolo di join.If the input was a Join, you need to create a new join symbol. In caso contrario, creare un simbolo normale.Otherwise, create a normal symbol.

    • Un extent, ad esempio un oggetto DbScanExpression, nel qual caso viene semplicemente aggiunto all'elenco di input dell'oggetto SqlSelectStatement del padre.An extent (a DbScanExpression, for example), in which case it is simply added to the list of inputs of the parent’s SqlSelectStatement.

    • Un oggetto diverso da SqlSelectStatement, nel qual caso viene racchiuso tra parentesi.Not a SqlSelectStatement, in which case it is wrapped with brackets.

    • Lo stesso oggetto SqlSelectStatement al quale viene aggiunto il padre.The same SqlSelectStatement to which the parent is added. In questo caso, i simboli dell'elenco FromExtents devono essere sostituiti da un unico nuovo oggetto JoinSymbol che li rappresenta tutti.In such case, the symbols in the FromExtents list need to be replaced with a single new JoinSymbol representing them all.

    • Per i primi tre casi viene chiamato il metodo AddFromSymbol per aggiungere la clausola AS e aggiornare la tabella dei simboli.For the first three cases, AddFromSymbol is called to add the AS clause, and update the symbol table.

Il terzo passaggio è costituito dalla visita della condizione di join (se presente).Third, the join condition (if any) is visited.

Operazioni sui setSet Operations

Le operazioni di impostazione DbUnionAllExpression, DbExceptExpression e DbIntersectExpression vengono elaborate dal metodo VisitSetOpExpression.The set operations DbUnionAllExpression, DbExceptExpression, and DbIntersectExpression are processed by the method VisitSetOpExpression. Tale metodo crea un SqlBuilder della formaIt creates a SqlBuilder of the shape

<leftSqlSelectStatement> <setOp> <rightSqlSelectStatement>  

Dove <leftSqlSelectStatement > e <rightSqlSelectStatement > sono oggetti SqlSelectStatements ottenuti mediante la visita di ognuno degli input, e <setOp > è l'operazione corrispondente (ad esempio UNION ALL).Where <leftSqlSelectStatement> and <rightSqlSelectStatement> are SqlSelectStatements obtained by visiting each of the inputs, and <setOp> is the corresponding operation (UNION ALL for example).

DbScanExpressionDbScanExpression

Se viene visitato in un contesto di join (come un input in un join che è un figlio del lato sinistro di un altro join), DbScanExpression restituisce un SqlBuilder con l'SQL di destinazione per la destinazione corrispondente, ovvero una visualizzazione, una tabella o una query di definizione.If visited in a join context (as an input to a join that is a left child of another join), DbScanExpression returns a SqlBuilder with the target SQL for the corresponding target, which is either a defining query, table, or a view. In caso contrario, viene creato un nuovo SqlSelectStatement con il campo FROM impostato in modo da corrispondere alla destinazione corrispondente.Otherwise, a new SqlSelectStatement is created with the FROM field set to correspond to the corresponding target.

DbVariableReferenceExpressionDbVariableReferenceExpression

La visita di un oggetto DbVariableReferenceExpression restituisce l'oggetto Symbol che corrisponde all'espressione di riferimento della variabile in base a una ricerca nella tabella dei simboli.The visit of a DbVariableReferenceExpression returns the Symbol corresponding to that variable reference expression based on a look up in the symbol table.

DbPropertyExpressionDbPropertyExpression

La bidimensionalità degli alias di join viene identificata ed elaborata durante la visita di un oggetto DbPropertyExpression.Join alias flattening is identified and processed when visiting a DbPropertyExpression.

Viene prima visitata la proprietà Instance e il risultato è un oggetto Symbol, JoinSymbol o SymbolPair.The Instance property is first visited and the result is a Symbol, a JoinSymbol, or a SymbolPair. I tre casi vengono gestiti nel modo seguente:Here is how these three cases are handled:

  • Se viene restituito un oggetto JoinSymbol, la rispettiva proprietà NameToExtent contiene un simbolo per la proprietà necessaria.If a JoinSymbol is returned, than its NameToExtent property contains a symbol for the needed property. Se il simbolo di join rappresenta un join annidato, viene restituita una nuova coppia di simboli con il simbolo di join per rilevare il simbolo che verrebbe usato come alias dell'istanza e il simbolo che rappresenta la proprietà effettiva per l'altre risoluzione.If the join symbol represents a nested join, a new Symbol pair is returned with the join symbol to track the symbol that would be used as the instance alias, and the symbol representing the actual property for further resolving.

  • Se viene restituito un oggetto SymbolPair e la parte della colonna è un simbolo di join, viene restituito nuovamente un simbolo di join, ma in questo caso la proprietà della colonna viene aggiornata in modo da puntare alla proprietà rappresentata dall'espressione della proprietà corrente.If a SymbolPair is returned and the Column part is a join symbol, a join symbol is again returned, but now the column property is updated to point to the property represented by the current property expression. In caso contrario, viene restituito un SqlBuilder con l'origine SymbolPair come alias e il simbolo per la proprietà corrente come colonna.Otherwise a SqlBuilder is returned with the SymbolPair source as the alias, and the symbol for the current property as the column.

  • Se viene restituito un oggetto Symbol, il metodo Visit restituisce un metodo SqlBuilder con l'istanza specifica come alias e il nome di proprietà come nome di colonna.If a Symbol is returned, the Visit method returns a SqlBuilder method with that instance as the alias, and the property name as column name.

DbNewInstanceExpressionDbNewInstanceExpression

Se viene usato come proprietà Projection di DbProjectExpression, DbNewInstanceExpression produce un elenco delimitato da virgole degli argomenti per rappresentare le colonne previste.When used as the Projection property of DbProjectExpression, DbNewInstanceExpression produces a comma-separated list of the arguments to represent the projected columns.

Quando DbNewInstanceExpression presenta un tipo restituito di raccolta e definisce una nuova raccolta delle espressioni fornite come argomenti, i tre casi seguenti vengono gestiti separatamente:When DbNewInstanceExpression has a collection return type, and defines a new collection of the expressions provided as arguments, the following three cases are handled separately:

  • Se l'unico argomento di DbNewInstanceExpression è DbElementExpression, viene convertito come segue:If DbNewInstanceExpression has DbElementExpression as the only argument, it is translated as follows:

    NewInstance(Element(X)) =>  SELECT TOP 1 …FROM X  
    

Se in DbNewInstanceExpression non sono presenti argomenti (rappresenta una tabella vuota), DbNewInstanceExpression viene convertito in:If DbNewInstanceExpression has no arguments (represents an empty table), DbNewInstanceExpression is translated into:

SELECT CAST(NULL AS <primitiveType>) as X  
FROM (SELECT 1) AS Y WHERE 1=0  

In caso contrario, DbNewInstanceExpression compila una scala union all degli argomenti:Otherwise DbNewInstanceExpression builds a union-all ladder of the arguments:

SELECT <visit-result-arg1> as X  
UNION ALL SELECT <visit-result-arg2> as X  
UNION ALL …  
UNION ALL SELECT <visit-result-argN> as X  

DbFunctionExpressionDbFunctionExpression

Le funzioni canoniche e quelle predefinite vengono elaborate allo stesso modo: se è necessaria una gestione speciale, ad esempio TRIM(string) in LTRIM(RTRIM(string), viene richiamato il gestore appropriato.Canonical and built-in functions are processed the same way: if they need special handling (TRIM(string) to LTRIM(RTRIM(string), for example), the appropriate handler is invoked. In caso contrario vengono convertite in FunctionName(arg1, arg2, ..., argn).Otherwise they are translated to FunctionName(arg1, arg2, ..., argn).

Vengono usati i dizionari per tenere traccia delle funzioni per cui è necessaria una gestione speciale e dei rispettivi gestori appropriati.Dictionaries are used to keep track of which functions need special handling and their appropriate handlers.

Le funzioni definite dall'utente vengono convertite in NamespaceName.FunctionName(arg1, arg2, ..., argn).User-defined functions are tanslated to NamespaceName.FunctionName(arg1, arg2, ..., argn).

DbElementExpressionDbElementExpression

Il metodo che visita DbElementExpression viene richiamato solo per visitare un oggetto DbElementExpression quando viene usato per rappresentare una sottoquery scalare.The method that visits DbElementExpression is only invoked for visiting a DbElementExpression when used to represent a scalar subquery. Pertanto, DbElementExpression viene convertito in un oggetto SqlSelectStatement completo e viene racchiuso tra parentesi.Therefore, DbElementExpression translates into a complete SqlSelectStatement and adds brackets around it.

DbQuantifierExpressionDbQuantifierExpression

A seconda del tipo di espressione (Any o All), DbQuantifierExpression viene convertito come:Depending on the expression type (Any or All), DbQuantifierExpression is translated it as:

Any(input, x) => Exists(Filter(input,x))  
All(input, x) => Not Exists(Filter(input, not(x))  

DbNotExpressionDbNotExpression

In alcuni casi è possibile comprimere la conversione di DbNotExpression con la rispettiva espressione di input.In some cases it is possible to collapse the translation of DbNotExpression with its input expression. Ad esempio:For example:

Not(IsNull(a)) =>  "a IS NOT NULL"  
Not(All(input, x) => Not (Not Exists(Filter(input, not(x))) => Exists(Filter(input, not(x))  

Il motivo per cui viene eseguita la seconda compressione è che sono state introdotte inefficienze dal provider durante la conversione dell'oggetto DbQuantifierExpression di tipo All.The reason the second collapse is performed is because inefficiencies were introduced by the provider when translating DbQuantifierExpression of type All. Ciò non ha consentito a Entity Framework di eseguire la semplificazione.Thus the Entity Framework could not have done the simplification.

DbIsEmptyExpressionDbIsEmptyExpression

DbIsEmptyExpression viene convertito come:DbIsEmptyExpression is translated as:

IsEmpty(inut) = Not Exists(input)  

Seconda fase della generazione SQL: generazione della stringa di comandoSecond Phase of SQL Generation: Generating the String Command

In caso di generazione di una stringa di comando SQL, SqlSelectStatement produce alias effettivi per i simboli che risolvono il problema della ridenominazione dei nomi di colonna e degli alias degli extent.When generating a string SQL command, the SqlSelectStatement produces actual aliases for the symbols, which addresses the issue of column name and extent alias renaming.

La ridenominazione degli alias degli extent si verifica durante la scrittura dell'oggetto SqlSelectStatement in una stringa.Extent alias renaming occurs while writing the SqlSelectStatement object into a string. Prima si crea un elenco di tutti gli alias usati dagli extent esterni.First create a list of all the aliases used by the outer extents. Ogni simbolo incluso in FromExtents (o AllJoinExtents se è non Null), viene rinominato se collide con alcuni degli extent esterni.Each symbol in the FromExtents (or AllJoinExtents if it is non-null), gets renamed if it collides with any of the outer extents. Se la ridenominazione è necessaria, non creerà conflitti con nessun extent raccolto in AllExtentNames.If renaming is needed, it will not conflict with any of the extents collected in AllExtentNames.

La ridenominazione delle colonne si verifica durante la scrittura di un oggetto Symbol in una stringa.Column renaming occurs while writing a Symbol object to a string. AddDefaultColumns nella prima fase ha determinato se è necessario rinominare un simbolo specifico di una colonna.AddDefaultColumns in the first phase has determined if a certain column symbol has to be renamed. Nella seconda fase viene eseguita solo la ridenominazione con la verifica che il nome prodotto non crei conflitti con uno dei nomi usati in AllColumnNamesIn the second phase only the rename occurs making sure that the name produced does not conflict with any name used in AllColumnNames

Per produrre nomi univoci sia per gli alias degli extent che per le colonne, usare <existing_name>_n, in cui n è l'alias più piccolo che non è stato ancora usato.To produce unique names both for extent aliases and for columns, use <existing_name>_n where n is the smallest alias that has not been used yet. L'elenco globale di tutti gli alias aumenta la necessità di eseguire ridenominazioni a catena.The global list of all aliases increases the need for cascading renames.

Vedere ancheSee Also

Generazione di comandi SQL nel provider di esempioSQL Generation in the Sample Provider