I'm trying to build simple sql query editor, I need to be able to edit query conditions (part after WHERE
keyword).
I have tree that shows conditions as shown below:
I have problem with transforming nodes to proper SQL code.
I can iterate over nodes, but this gives me below result:
AND
Name = John
OR
Surname = Smith
Lastname = Smith
But I need this (valid SQL):
Name = John
AND
(
Surname = Smith
OR
Lastname = Smith
)
How can I do that?
Here is code that I used to extend TreeNode and to create sample from screenshot:
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();
}
}
There are two different types of nodes: OperatorNode
and SqlTextNode
. You can easily check the type of the node and execute the corresponding actions:
If the node is SqlTextNode
just print it as you did it before.
If the node is OperatorNode
you need to cover all children with ()
and insert the operator between them.
This algorithm can be implemented in many different ways but I will add a simple example:
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 + ")");
}
}
If you want to add indentation into the final query you could pass throuh the reqursion function ( CallRecursive
) as a parameter and increase it when you need.
This is my version of answer:
I've overridden ToString
in my custom node types:
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);
}
}
This way each node has logic responsible for creating query parts.
Having that I can generate full query using below function:
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;
}
Output doesn't have indentations, but I don't have to show final query, this is just temporary for debug.
I'm aware that I must escape conditions values with single quotes, but that's the easy part :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.