Skip to content

Commit a914698

Browse files
committed
Update README.md with new features and examples
- Added badges for license, code size, last commit, and NuGet version. - Organized and numbered sections for SQL query parsing and modification. - Clarified expected SQL output and improved code examples. - Introduced new features for modifying columns, managing UNION queries, and filtering with outer joins. - Highlighted the benefits of CarbunqleX for SQL maintainability and reusability.
1 parent de73141 commit a914698

File tree

1 file changed

+101
-13
lines changed

1 file changed

+101
-13
lines changed

README.md

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# CarbunqleX - SQL Parser and Modeler
1+
# CarbunqleX - SQL Parser and Modeler
22

33
![GitHub](https://img.shields.io/github/license/mk3008/Carbunqlex)
44
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/mk3008/Carbunqlex)
@@ -32,15 +32,15 @@ Unlike conventional SQL libraries, CarbunqleX automatically determines the most
3232

3333
## 📦 Installation
3434

35-
To install [Carbunqlex](https://www.nuget.org/packages/Carbunqlex/), use the following command:
35+
[Carbunqlex](https://www.nuget.org/packages/Carbunqlex/) can be installed from NuGet. To install using the package manager, use the following command:
3636

3737
```sh
38-
PM> NuGet\Install-Package Carbunqlex
38+
NuGet\Install-Package Carbunqlex
3939
```
4040

4141
## 📖 Documentation
4242

43-
### 1️⃣ Parsing a SQL Query
43+
### 1. Parsing a SQL Query
4444

4545
Let's start by parsing a simple SQL query into an AST using `QueryAstParser.Parse`. We will then convert it back to SQL with `ToSql` and inspect its structure using `ToTreeString`.
4646

@@ -60,9 +60,15 @@ Console.WriteLine("* AST");
6060
Console.WriteLine(query.ToTreeString());
6161
```
6262

63-
This basic example demonstrates how CarbunqleX converts SQL into a structured AST representation, making it easier to analyze and manipulate queries programmatically.
63+
#### 🔍 Expected SQL Output
64+
65+
```sql
66+
SELECT a.table_a_id, a.value FROM table_a AS a
67+
```
68+
69+
This makes it easy to convert between them.
6470

65-
### 2️⃣ Modifying the WHERE Clause
71+
### 2. Modifying the WHERE Clause
6672

6773
Now, let's modify the query by injecting a `WHERE` condition.
6874

@@ -83,11 +89,11 @@ FROM table_a AS a
8389
WHERE a.value = 1;
8490
```
8591

86-
CarbunqleX automatically determines where to insert the condition while maintaining SQL integrity.
92+
CarbunqleX allows you to inject conditions while still maintaining the integrity of your SQL.
8793

88-
### 3️⃣ Handling CTEs and Subqueries
94+
### 3. Handling CTEs and Subqueries
8995

90-
Let's take it a step further by injecting a `WHERE` condition into a query that includes **CTEs and subqueries**.
96+
Let's try to insert a `WHERE` condition into a query that contains a **CTE and a subquery**.
9197

9298
```csharp
9399
var query = QueryAstParser.Parse("""
@@ -129,11 +135,11 @@ WHERE orders.region IN (SELECT x.region FROM top_regions x)
129135
GROUP BY orders.region, orders.product;
130136
```
131137

132-
CarbunqleX **intelligently places the condition in the deepest relevant query**, ensuring correctness.
138+
CarbunqleX will **intelligently place the condition in the deepest related query**. It will also **dynamically merge CTEs**.
133139

134-
### 4️⃣ Advanced Filtering
140+
### 4. Standardizing filtering
135141

136-
Now, let's introduce a **more advanced use case** where we dynamically filter data based on user permissions. We'll define a reusable function to retrieve regions a user has access to and use it in a filtering condition.
142+
Next, we will introduce a more advanced use case that dynamically filters data based on user permissions. We define the areas that users can access as reusable functions.
137143

138144
#### 🔧 Define a Subquery Function
139145

@@ -186,7 +192,89 @@ WHERE orders.region IN (SELECT x.region FROM top_regions AS x)
186192
GROUP BY orders.region, orders.product
187193
```
188194

189-
By dynamically injecting permission-based filtering, we can ensure **secure and flexible query customization**.
195+
By making filtering a function, we can express queries that are **highly maintainable and versatile**.
196+
197+
### **5. Modify columns**
198+
199+
This feature is useful for enforcing constraints such as **closing date control in accounting** by ensuring a column value does not fall below a specified threshold.
200+
201+
```csharp
202+
var query = QueryAstParser.Parse("SELECT s.sale_date, s.sales_amount FROM sales AS s");
203+
204+
// Ensure sale_date is at least '2024-01-01'
205+
query.ModifyColumn("sale_date", c => c.Greatest(new DateTime(2024, 1, 1)));
206+
207+
Console.WriteLine(query.ToSql());
208+
```
209+
210+
#### 🔍 Expected SQL Output
211+
212+
```sql
213+
SELECT GREATEST(s.sale_date, '2024-01-01 00:00:00') AS sale_date, s.sales_amount
214+
FROM sales AS s;
215+
```
216+
217+
In this way, column processing can be made common.
218+
219+
### **6. Manage UNION queries as modular components**
220+
221+
This approach allows you to manage `UNION` queries as separate components, improving maintainability and modularity.
222+
223+
```csharp
224+
var query1 = QueryAstParser.Parse("SELECT id FROM table_a");
225+
var query2 = QueryAstParser.Parse("SELECT id FROM table_b");
226+
227+
// Create a UNION ALL query and wrap it as a distinct subquery
228+
var distinctQuery = query1.UnionAll(query2).ToSubQuery().Distinct();
229+
230+
Console.WriteLine(distinctQuery.ToSql());
231+
```
232+
233+
#### 🔍 Expected SQL Output
234+
235+
```sql
236+
SELECT DISTINCT * FROM (SELECT id FROM table_a UNION ALL SELECT id FROM table_b) AS d;
237+
```
238+
239+
Now managing huge union queries is not scary.
240+
241+
### 7. Filtering using outer joins**
242+
243+
You can also dynamically insert outer join conditions to perform filtering.
244+
245+
```csharp
246+
var query = QueryAstParser.Parse("""
247+
WITH regional_sales AS (
248+
SELECT orders.region, SUM(orders.amount) AS total_sales
249+
FROM orders
250+
GROUP BY orders.region
251+
), top_regions AS (
252+
SELECT rs.region
253+
FROM regional_sales rs
254+
WHERE rs.total_sales > (SELECT SUM(x.total_sales)/10 FROM regional_sales x)
255+
)
256+
SELECT orders.region,
257+
orders.product,
258+
SUM(orders.quantity) AS product_units,
259+
SUM(orders.amount) AS product_sales
260+
FROM orders
261+
WHERE orders.region IN (SELECT x.region FROM top_regions x)
262+
GROUP BY orders.region, orders.product
263+
""");
264+
265+
// Set isCurrentOnly: false to use the join as a filter condition
266+
query.From("region", isCurrentOnly: false, static from =>
267+
{
268+
from.LeftJoin("top_regions", "tp");
269+
from.EditQuery(q =>
270+
{
271+
q.Where("tp.region is null");
272+
});
273+
});
274+
275+
// left join top_regions as tp on orders.region = tp.region where tp.region is null
276+
var expected = "with regional_sales as (select orders.region, SUM(orders.amount) as total_sales from orders left join top_regions as tp on orders.region = tp.region where tp.region is null group by orders.region), top_regions as (select rs.region from regional_sales as rs where rs.total_sales > (select SUM(x.total_sales) / 10 from regional_sales as x)) select orders.region, orders.product, SUM(orders.quantity) as product_units, SUM(orders.amount) as product_sales from orders where orders.region in (select x.region from top_regions as x) group by orders.region, orders.product";
277+
```
190278

191279
## 📌 Conclusion
192280

0 commit comments

Comments
 (0)