TSqlFragment 内の ColumnReferenceExpression をスキャンするビジターを使用する場合、実際にはそうではない列としてピックアップされるものがいくつかあります。たとえば、次の sql では:
select Age, FirstName, LastName, DATEADD(year, Age, getdate())
from table1
inner join table2 on 1 = 1
yearパラメーターは、Age、FirstName、および LastName と共に列参照として取得されます。年を他の列と区別する方法はないようです。
私が尋ねている理由は、選択に複数のテーブルがあり、これらの非列も選択している場合に、2 つの部分名をチェックする SqlCodeAnalysisRule を作成したためです。誰かがそれらを除外する方法についてアイデアを持っている場合に備えて、ルールの Analyze を次に示します。
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; }
}
見つかった各列のすべてのプロパティを非列と比較しましたが、それらを除外するために使用できる違いはありません。