0

当使用访问者扫描 TSqlFragment 中的 ColumnReferenceExpression 时,有些东西会被视为真正不是的列。例如在这个 sql 中:

select Age, FirstName, LastName, DATEADD(year, Age, getdate())   
from table1 
inner join table2 on 1 = 1

year参数与 Age、FirstName 和 LastName 一起被用作列引用。似乎没有任何方法可以将年份与其他列区分开来。

The reason I am asking is that we have written a SqlCodeAnalysisRule to check for two part names when there is more than one table in a select and it is picking up these non-columns as well. 如果有人对如何排除它们有任何想法,请在这里分析规则:

public override IList<SqlRuleProblem> Analyze(SqlRuleExecutionContext ruleExecutionContext)
{
    var problems = new List<SqlRuleProblem>();
    var sqlObj = ruleExecutionContext.ModelElement;
    if (sqlObj == null) { return problems; }

    var fragment = ruleExecutionContext.ScriptFragment;
    var selectStatementVisitor = new SelectStatementVisitor();
    fragment.Accept(selectStatementVisitor);

    if (selectStatementVisitor.Statements.Count == 0) { return problems; }

    foreach (var select in selectStatementVisitor.Statements)
    {
        var fromClause = (select.QueryExpression as QuerySpecification)?.FromClause;
        if (fromClause == null) { continue; }
        //check to ensure we have more than one table
        var namedTableVisitor = new NamedTableReferenceVisitor();
        fromClause.Accept(namedTableVisitor);
        if(namedTableVisitor.Statements.Count <= 1) { continue; }

        var columnReferences = new ColumnReferenceExpressionVisitor();
        select.Accept(columnReferences);

        //TODO: This will erroneously pickup things which appear to be column references as well
        //      such as the 'dd' in DATEADD(dd, rev.ReviewReferenceDaysStart, @StartDate)
        var offenders = columnReferences.Statements
            .Where(c => (c as ColumnReferenceExpression).MultiPartIdentifier?.Identifiers.Count == 1)
            .Select(n => (n as ColumnReferenceExpression).MultiPartIdentifier.Identifiers[0]);

        problems.AddRange(offenders.Select(cr => new SqlRuleProblem(string.Format(Message, cr.Value), sqlObj, cr)));
    }
    return problems;
}

我们所有的访客都遵循几乎相同的模式。以下是列参考访问者作为其他提及的示例:

internal class ColumnReferenceExpressionVisitor : TSqlFragmentVisitor, IVisitor<ColumnReferenceExpression>
{
    public IList<ColumnReferenceExpression> Statements { get; } = new List<ColumnReferenceExpression>();

    public override void ExplicitVisit(ColumnReferenceExpression node)
    {
        Statements.Add(node);
    }
}   

public interface IVisitor<T> where T : TSqlFragment
{
    IList<T> Statements { get; }
}   

我已经将找到的每个列的所有属性与非列进行了比较,并且没有什么不同可以用来排除我看到的它们。

4

1 回答 1

1

dd 是列引用,因为您正在获取树中的所有 ColumnReferences - 脚本 dom 不知道 dateadd 是什么,它只知道它是一个函数,因此 dd 很可能是一个列。

如果是我,我会得到 select 语句,获取 SelectElements 并遍历它们(例如,如果你有相关的子查询,通过使用 Select 访问者你仍然应该得到所有这些),在这种情况下忽略任何不是列引用。

手动进行(而不是 linq),这样会更清晰一些,如下所示:

foreach (var select in selectStatementVisitor.Statements)
        {

            var columnReferences = new ColumnReferenceExpressionVisitor();
            select.Accept(columnReferences);

            foreach (var item in (select.QueryExpression as QuerySpecification).SelectElements)
            {

//other code here if you want ...

                if (item is SelectScalarExpression)
                {
                    var expression = item as SelectScalarExpression;
                    if (expression.Expression is ColumnReferenceExpression)
                    {
                        var column = expression.Expression as ColumnReferenceExpression;
                        Console.WriteLine(column); // <-- this is only ColumnReferenceExpression's
                    }
                }
            }

        }
于 2017-05-19T14:38:10.230 回答