繁体   English   中英

从TreeView节点构建SQL查询

[英]Build SQL Query from TreeView nodes

我正在尝试构建简单的sql查询编辑器,我需要能够编辑查询条件(在WHERE关键字之后的部分)。
我有显示条件的树,如下所示:

在此处输入图片说明

我在将节点转换为正确的SQL代码时遇到问题。

我可以遍历节点,但这给了我以下结果:

AND
Name = John
OR
Surname = Smith
Lastname = Smith

但是我需要这个(有效的SQL):

Name = John
AND
(
    Surname = Smith
    OR
    Lastname = Smith
)

我怎样才能做到这一点?

这是我用来扩展TreeNode并从屏幕快照创建示例的代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        var root = new OperatorNode(OperatorType.AND);
        var treeNode = new SqlTextNode("Name", ContitionType.EQUAL, "John");
        root.Nodes.Add(treeNode);
        var orNode = new OperatorNode(OperatorType.OR);
        var node2 = new SqlTextNode("Surname",ContitionType.EQUAL, "Smith");
        var node3 = new SqlTextNode("Lastname",ContitionType.EQUAL, "Smith");
        orNode.Nodes.Add(node2);
        orNode.Nodes.Add(node3);
        root.Nodes.Add(orNode);
        treeView1.Nodes.Add(root);
        treeView1.ExpandAll();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        CallRecursive(treeView1);
    }

    private void PrintRecursive(TreeNode treeNode)
    {
        Debug.WriteLine(treeNode.Text);
        foreach (TreeNode tn in treeNode.Nodes)
        {
            PrintRecursive(tn);
        }
    }

    private void CallRecursive(TreeView treeView)
    {
        TreeNodeCollection nodes = treeView.Nodes;
        foreach (TreeNode n in nodes)
        {
            PrintRecursive(n);
        }
    }
}

public class OperatorNode : TreeNode
{
    private OperatorType _operator;

    public OperatorType Operator
    {
        get { return _operator; }
        set
        {
            if(value==_operator) return;
            _operator = value;
            Text = _operator.ToString();
        }
    }

    public OperatorNode(OperatorType @operator) : base(@operator.ToString())
    {
        _operator = @operator;
    }
}

public class SqlTextNode : TreeNode
{
    private string _fieldName;
    private ContitionType _condition;
    private string _value;


    public SqlTextNode(string fieldName, ContitionType condition, string value)
    {
        _fieldName = fieldName;
        _condition = condition;
        _value = value;
        UpdateText();
    }

    public string FieldName
    {
        get { return _fieldName; }
        set
        {
            if (value == _fieldName) return;
            _fieldName = value;
            UpdateText();
        }
    }

    public ContitionType Condition
    {
        get { return _condition; }
        set
        {
            if (value == _condition) return;
            _condition = value;
            UpdateText();
        }
    }

    public string Value
    {
        get { return _value; }
        set
        {
            if (value == _value) return;
            _value = value;
            UpdateText();
        }
    }

    private void UpdateText()
    {
        Text = string.Format("{0} {1} {2}", _fieldName, Enums.GetEnumDescription(_condition), _value);
    }
}

public enum OperatorType
{
    AND,
    OR
}

public enum ContitionType
{
    EQUAL,
    NOT_EQUAL
}

public static class Enums
{
    public static string GetEnumDescription(Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes =
            (DescriptionAttribute[])fi.GetCustomAttributes(
            typeof(DescriptionAttribute),
            false);

        return attributes.Length > 0 ? attributes[0].Description : value.ToString();
    }
}

有两种不同类型的节点: OperatorNodeSqlTextNode 您可以轻松检查节点的类型并执行相应的操作:

  • 如果该节点是SqlTextNode只需像SqlTextNode一样打印它即可。

  • 如果节点是OperatorNode ,则需要用()覆盖所有子代,然后在它们之间插入运算符。

该算法可以通过许多不同的方式实现,但我将添加一个简单的示例:

private void PrintRecursive(TreeNode treeNode, int indentation)
{
    var indent = new string(' ', indentation * IndentSize);
    // SqlTextNode doesn't have children
    if (treeNode is SqlTextNode)
        Debug.WriteLine(indent + treeNode.Text);
    else
    {
        Debug.WriteLine(indent + "(");
        for (int i=0; i<treeNode.Nodes.Count; i++ )
        { 
            PrintRecursive(treeNode.Nodes[i], indentation+1);
            if (i!=treeNode.Nodes.Count-1)
            {
                // print the operator
                indent = new string(' ', (indentation+1) * IndentSize);                
                Debug.WriteLine(indent + treeNode.Text);
            }
        }
        Debug.WriteLine(indent + ")");
    }
}

如果要将缩进添加到最终查询中,则可以通过reqursion函数( CallRecursive )作为参数,并在需要时增加它。

这是我的答案版本:
我在自定义节点类型中覆盖了ToString

public class OperatorNode : TreeNode
{
    private OperatorType _operator;

    public OperatorType Operator
    {
        get { return _operator; }
        set
        {
            if(value==_operator) return;
            _operator = value;
            Text = _operator.ToString();
        }
    }

    public OperatorNode(OperatorType @operator) : base(@operator.ToString())
    {
        _operator = @operator;
    }

    public override string ToString()
    {
        List<string> n = (from TreeNode node in Nodes select node.ToString()).ToList();

        return " ( " + string.Join(Environment.NewLine + _operator + " ", n) + " ) ";
    }
}

public class SqlTextNode : TreeNode
{
    private string _fieldName;
    private ContitionType _condition;
    private string _value;


    public SqlTextNode(string fieldName, ContitionType condition, string value)
    {
        _fieldName = fieldName;
        _condition = condition;
        _value = value;

        UpdateText();
    }

    public override string ToString()
    {
        return string.Format("{0} {1} {2}", _fieldName, _condition.GetDescription(), _value);
    }

    public string FieldName
    {
        get { return _fieldName; }
        set
        {
            if (value == _fieldName) return;
            _fieldName = value;
            UpdateText();
        }
    }

    public ContitionType Condition
    {
        get { return _condition; }
        set
        {
            if (value == _condition) return;
            _condition = value;
            UpdateText();
        }
    }

    public string Value
    {
        get { return _value; }
        set
        {
            if (value == _value) return;
            _value = value;
            UpdateText();
        }
    }

    private void UpdateText()
    {
        Text = string.Format("{0} {1} {2}", _fieldName, _condition.GetDescription(), _value);
    }
}

这样,每个节点都有负责创建查询部分的逻辑。
有了它,我可以使用以下功能生成完整的查询:

private void PrintQuery(TreeView treeView)
{
    string s = string.Empty;
    TreeNodeCollection nodes = treeView.Nodes;
    s = nodes.Cast<TreeNode>().Aggregate(s, (current, n) => current + (n.ToString() + Environment.NewLine)).Trim();

    //remove unwanted brackets
    s = s.Remove(s.Length - 1).Substring(1).Trim();
    textBox1.Text = s;
}

输出没有缩进,但是我不必显示最终查询,这只是调试的临时结果。

我知道我必须用单引号将条件值转义,但这很容易:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM