sql-blocks 1.25.2__py3-none-any.whl → 1.25.47__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sql_blocks/sql_blocks.py +577 -123
- {sql_blocks-1.25.2.dist-info → sql_blocks-1.25.47.dist-info}/METADATA +295 -5
- sql_blocks-1.25.47.dist-info/RECORD +7 -0
- sql_blocks-1.25.2.dist-info/RECORD +0 -7
- {sql_blocks-1.25.2.dist-info → sql_blocks-1.25.47.dist-info}/LICENSE +0 -0
- {sql_blocks-1.25.2.dist-info → sql_blocks-1.25.47.dist-info}/WHEEL +0 -0
- {sql_blocks-1.25.2.dist-info → sql_blocks-1.25.47.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 1.25.
|
3
|
+
Version: 1.25.47
|
4
4
|
Summary: Allows you to create objects for parts of SQL query commands. Also to combine these objects by joining them, adding or removing parts...
|
5
5
|
Home-page: https://github.com/julio-cascalles/sql_blocks
|
6
6
|
Author: Júlio Cascalles
|
@@ -52,6 +52,12 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
52
52
|
...should return:
|
53
53
|
**SELECT extract(year from due_date) as YEAR_ref...**
|
54
54
|
|
55
|
+
Possible tags in ExpressionField:
|
56
|
+
* {f} - The field name;
|
57
|
+
* {af} - The field name preceded by the table alias;
|
58
|
+
> Can be written as {a.f} or %
|
59
|
+
* {t} - The table name;
|
60
|
+
* {a} - Only the table alias.
|
55
61
|
---
|
56
62
|
|
57
63
|
### 3 - To set conditions, use **Where**:
|
@@ -67,6 +73,16 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
67
73
|
3.1 -- If you want to filter the field on a range of values:
|
68
74
|
|
69
75
|
`a = Select( 'Actor a', age=Between(45, 69) )`
|
76
|
+
...but if it is a time slot within the same day, you can do it like this:
|
77
|
+
`Select(..., event_date=SameDay("2024-10-03"))`
|
78
|
+
This results in
|
79
|
+
```
|
80
|
+
SELECT ...
|
81
|
+
WHERE
|
82
|
+
event_date >= '2024-10-03 00:00:00' AND
|
83
|
+
event_date <= '2024-10-03 23:59:59'
|
84
|
+
```
|
85
|
+
---
|
70
86
|
|
71
87
|
3.2 -- Sub-queries:
|
72
88
|
```
|
@@ -97,8 +113,10 @@ query = Select('Movie m', title=Field,
|
|
97
113
|
genre=eq("Sci-Fi"),
|
98
114
|
awards=contains("Oscar")
|
99
115
|
)
|
116
|
+
AND=Options(
|
117
|
+
..., name=startswith('Chris')
|
118
|
+
)
|
100
119
|
```
|
101
|
-
> Could be AND=Options(...)
|
102
120
|
|
103
121
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
104
122
|
```
|
@@ -110,6 +128,16 @@ based_on_book=Not.is_null()
|
|
110
128
|
hash_tag=inside(['space', 'monster', 'gore'])
|
111
129
|
```
|
112
130
|
|
131
|
+
3.6 -- Combining ExpressionField with Where condition:
|
132
|
+
* The **formula** method allows you to write an expression as a condition:
|
133
|
+
```
|
134
|
+
query=Select(
|
135
|
+
'Folks f2',
|
136
|
+
id=Where.formula('({af} = a.father OR {af} = a.mother)')
|
137
|
+
)
|
138
|
+
```
|
139
|
+
> Results: `WHERE...f2.id = a.father OR f2.id = a.mother`
|
140
|
+
|
113
141
|
---
|
114
142
|
### 4 - A field can be two things at the same time:
|
115
143
|
|
@@ -138,6 +166,35 @@ FROM
|
|
138
166
|
JOIN Cast c ON (a.cast = c.id)
|
139
167
|
```
|
140
168
|
|
169
|
+
---
|
170
|
+
**5.1 Multiple tables without JOIN**
|
171
|
+
> Warning: This is **NOT** recommended! ⛔
|
172
|
+
|
173
|
+
|
174
|
+
#### Example:
|
175
|
+
singer = Select(
|
176
|
+
"Singer artist", id=PrimaryKey,
|
177
|
+
name=NamedField('artist_name')
|
178
|
+
)
|
179
|
+
album = Select (
|
180
|
+
"Album album",
|
181
|
+
name=NamedField('album_name'),
|
182
|
+
artist_id=Where.join(singer), # <===== 👀
|
183
|
+
)
|
184
|
+
**>> print(query)**
|
185
|
+
|
186
|
+
SELECT
|
187
|
+
album.name as album_name,
|
188
|
+
artist.name as artist_name,
|
189
|
+
album.year_recorded
|
190
|
+
FROM
|
191
|
+
Album album
|
192
|
+
,Singer artist
|
193
|
+
WHERE
|
194
|
+
(album.artist_id = artist.id)
|
195
|
+
|
196
|
+
|
197
|
+
|
141
198
|
---
|
142
199
|
### 6 - The reverse process (parse):
|
143
200
|
```
|
@@ -258,6 +315,30 @@ m = Select...
|
|
258
315
|
>> **m + c => Ok!**
|
259
316
|
|
260
317
|
|
318
|
+
---
|
319
|
+
**8.3 Difference between queries**
|
320
|
+
```
|
321
|
+
STATUS_DELIVERED_OK = 93
|
322
|
+
orders = Select('orders',
|
323
|
+
customer_id=ForeignKey('customers'),
|
324
|
+
status=eq(STATUS_DELIVERED_OK)
|
325
|
+
)
|
326
|
+
customers = Select('customers'
|
327
|
+
id=PrimaryKey, name=Field
|
328
|
+
)
|
329
|
+
gap = orders - customers
|
330
|
+
```
|
331
|
+
return _customers without orders_:
|
332
|
+
|
333
|
+
SELECT
|
334
|
+
c.name
|
335
|
+
FROM
|
336
|
+
customers c
|
337
|
+
WHERE
|
338
|
+
NOT c.id IN (
|
339
|
+
SELECT o.customer_id FROM orders o
|
340
|
+
WHERE o.status = 93
|
341
|
+
)
|
261
342
|
---
|
262
343
|
|
263
344
|
### 9 - Comparing objects
|
@@ -363,6 +444,35 @@ m2 = Select(
|
|
363
444
|
|
364
445
|
> The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
|
365
446
|
|
447
|
+
>> NOTE: When a joined table is used only as a filter, it is possible that it can be changed to a sub-query:
|
448
|
+
|
449
|
+
query = Select(
|
450
|
+
'Installments i', due_date=Field, customer=Select(
|
451
|
+
'Customer c', id=PrimaryKey,
|
452
|
+
name=endswith('Smith')
|
453
|
+
)
|
454
|
+
)
|
455
|
+
print(query)
|
456
|
+
print('-----')
|
457
|
+
query.optimize([RuleReplaceJoinBySubselect])
|
458
|
+
print(query)
|
459
|
+
```
|
460
|
+
SELECT
|
461
|
+
i.due_date
|
462
|
+
FROM
|
463
|
+
Installments i
|
464
|
+
JOIN Customer c ON (i.customer = c.id)
|
465
|
+
WHERE
|
466
|
+
c.name LIKE '%Smith'
|
467
|
+
-----
|
468
|
+
SELECT
|
469
|
+
i.due_date
|
470
|
+
FROM
|
471
|
+
Installments i
|
472
|
+
WHERE
|
473
|
+
i.customer IN (SELECT c.id FROM Customer c WHERE c.name LIKE '%Smith')
|
474
|
+
```
|
475
|
+
|
366
476
|
---
|
367
477
|
|
368
478
|
### 12 - Adding multiple fields at once
|
@@ -370,7 +480,7 @@ m2 = Select(
|
|
370
480
|
query = Select('post p')
|
371
481
|
query.add_fields(
|
372
482
|
'user_id, created_at',
|
373
|
-
|
483
|
+
[OrderBy, GroupBy]
|
374
484
|
)
|
375
485
|
```
|
376
486
|
...is the same as...
|
@@ -419,6 +529,7 @@ ORDER BY
|
|
419
529
|
* `^` Put the field in the ORDER BY clause
|
420
530
|
* `@` Immediately after the table name, it indicates the grouping field.
|
421
531
|
* `$` For SQL functions like **avg**$_field_, **sum**$_field_, **count**$_field_...
|
532
|
+
* `*` Sets the primary key field.
|
422
533
|
|
423
534
|
|
424
535
|
---
|
@@ -520,12 +631,18 @@ It consists of the inverse process of parsing: From a Select object, it returns
|
|
520
631
|
---
|
521
632
|
### 14 - Window Function
|
522
633
|
|
523
|
-
Aggregation functions (Avg, Min, Max, Sum, Count) have the **over** method...
|
634
|
+
Aggregation functions (Avg, Min, Max, Sum, Count) -- or Window functions (Lead, Lag, Row_Number, Rank) -- have the **over** method...
|
524
635
|
|
525
636
|
query=Select(
|
526
637
|
'Enrollment e',
|
527
638
|
payment=Sum().over(
|
528
|
-
|
639
|
+
student_id=Partition, due_date=OrderBy,
|
640
|
+
# _=Rows(Current(), Following(5)),
|
641
|
+
# ^^^-------> ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING
|
642
|
+
# _=Rows(Preceding(3), Following()),
|
643
|
+
# ^^^-------> ROWS BETWEEN 3 PRECEDING AND UNBOUNDED FOLLOWING
|
644
|
+
# _=Rows(Preceding(3))
|
645
|
+
# ^^^-------> ROWS 3 PRECEDING
|
529
646
|
).As('sum_per_student')
|
530
647
|
)
|
531
648
|
|
@@ -567,3 +684,176 @@ GROUP BY
|
|
567
684
|
ORDER BY
|
568
685
|
customer_count
|
569
686
|
```
|
687
|
+
---
|
688
|
+
### 16 - Function classes
|
689
|
+
You may use this functions:
|
690
|
+
* SubString
|
691
|
+
* Round
|
692
|
+
* DateDiff
|
693
|
+
* Year
|
694
|
+
* Current_Date
|
695
|
+
* Avg
|
696
|
+
* Min
|
697
|
+
* Max
|
698
|
+
* Sum
|
699
|
+
* Count
|
700
|
+
* Lag
|
701
|
+
* Lead
|
702
|
+
* Row_Number
|
703
|
+
* Rank
|
704
|
+
* Coalesce
|
705
|
+
* Cast
|
706
|
+
> Some of these functions may vary in syntax depending on the database.
|
707
|
+
For example, if your query is going to run on Oracle, do the following:
|
708
|
+
|
709
|
+
`Function.dialect = Dialect.ORACLE`
|
710
|
+
|
711
|
+
|
712
|
+
> Most of this functions you can use nested inside each other.
|
713
|
+
*Example:*
|
714
|
+
```
|
715
|
+
Select(...
|
716
|
+
event_date=Substring(
|
717
|
+
Cast("CHAR"), 12, 19
|
718
|
+
).As('time')
|
719
|
+
)
|
720
|
+
```
|
721
|
+
Results...
|
722
|
+
```
|
723
|
+
SELECT ...
|
724
|
+
SubString(Cast(event_date As char), 12, 19) as time
|
725
|
+
```
|
726
|
+
|
727
|
+
>> `Function.auto_convert` option (default: True)
|
728
|
+
|
729
|
+
- Put Cast(...) when there is a difference between the types of the parameter and the return of the nested function
|
730
|
+
```
|
731
|
+
birth=Round( DateDiff(Current_Date()) ).As('age')
|
732
|
+
```
|
733
|
+
...Returns...
|
734
|
+
```
|
735
|
+
SELECT
|
736
|
+
Round(
|
737
|
+
Cast(Current_Date() - p.birth As FLOAT)
|
738
|
+
/* ^^^ */
|
739
|
+
) as age
|
740
|
+
...
|
741
|
+
```
|
742
|
+
---
|
743
|
+
|
744
|
+
### 17 - CTE and Recursive classes
|
745
|
+
|
746
|
+
* **17.1 - _CTE class_**
|
747
|
+
```
|
748
|
+
query = Select(
|
749
|
+
'SocialMedia s', post=Count, reaction=Sum, user=GroupBy
|
750
|
+
)
|
751
|
+
print( CTE('Metrics', [query]) )
|
752
|
+
```
|
753
|
+
The result is...
|
754
|
+
```
|
755
|
+
WITH Metrics AS (
|
756
|
+
SELECT Count(s.post), Sum(s.reaction) FROM SocialMedia s GROUP BY user
|
757
|
+
)SELECT * FROM Metrics
|
758
|
+
```
|
759
|
+
|
760
|
+
* **17.2 - _Recursive class_**
|
761
|
+
```
|
762
|
+
q1 = Select(
|
763
|
+
'SocialMedia me', name=[ eq(MY_NAME), Field ]
|
764
|
+
)
|
765
|
+
q2 = Select(
|
766
|
+
'SocialMedia you' name=Field, id=Where.formula('{af} = n.friend')
|
767
|
+
)
|
768
|
+
print( Recursive('Network', [q1, q2]) )
|
769
|
+
```
|
770
|
+
The result is...
|
771
|
+
```
|
772
|
+
WITH RECURSIVE Network AS (
|
773
|
+
SELECT me.name FROM SocialMedia me WHERE
|
774
|
+
me.name = 'Júlio Cascalles'
|
775
|
+
UNION ALL
|
776
|
+
SELECT you.name FROM SocialMedia you , Network n
|
777
|
+
WHERE you.id = n.friend
|
778
|
+
)SELECT * FROM Network
|
779
|
+
```
|
780
|
+
|
781
|
+
* **17.2.1 - The `create` method** ... parameters :
|
782
|
+
- name: The name of the CTE
|
783
|
+
- pattern: A cypher script that defines the tables used
|
784
|
+
- formula: The format for `Where.formula` method _(*)_
|
785
|
+
- init_value: The value for the condition in the first table
|
786
|
+
- format (optional): If tables are files or internet hiperlinks, you may especify the extension and/or folder...
|
787
|
+
> Example:
|
788
|
+
```
|
789
|
+
R = Recursive.create(
|
790
|
+
'Route R', 'Flyght(departure, arrival)',
|
791
|
+
'[2] = R.[1]', 'JFK', format='.csv'
|
792
|
+
) # ^^^--- Flyghts from JFK airport
|
793
|
+
```
|
794
|
+
_...Creates a recursive CTE called Route, using Flyght table, where the recursivity condition is Flyght.arrival equals to Route.departure_
|
795
|
+
>> (*) -- Note that [1] and [2] refers to first field and second field. 😉
|
796
|
+
|
797
|
+
Result:
|
798
|
+
|
799
|
+
WITH RECURSIVE Route AS (
|
800
|
+
SELECT f1.departure, f1.arrival
|
801
|
+
FROM Flyght.csv f1
|
802
|
+
WHERE f1.departure = 'JFK'
|
803
|
+
UNION ALL
|
804
|
+
SELECT f2.departure, f2.arrival
|
805
|
+
FROM Flyght.csv f2
|
806
|
+
, Route R
|
807
|
+
WHERE f2.arrival = R.departure
|
808
|
+
)SELECT * FROM Route R
|
809
|
+
|
810
|
+
**17.2.2 - The `join` method**
|
811
|
+
|
812
|
+
In the previous example, if you add this code...
|
813
|
+
`R.join('Airport(*id,name)', 'departure, arrival', format='.csv')`
|
814
|
+
|
815
|
+
...The result would be:
|
816
|
+
|
817
|
+
WITH RECURSIVE Route AS (
|
818
|
+
SELECT f1.departure, f1.arrival
|
819
|
+
FROM Flyght.csv f1
|
820
|
+
WHERE f1.departure = 'JFK'
|
821
|
+
UNION ALL
|
822
|
+
SELECT f2.departure, f2.arrival
|
823
|
+
FROM Flyght.csv f2
|
824
|
+
, Route R
|
825
|
+
WHERE f2.arrival = R.departure
|
826
|
+
)SELECT
|
827
|
+
a1.name, a2.name
|
828
|
+
FROM
|
829
|
+
Route R
|
830
|
+
JOIN Airport.csv a2 ON (R.arrival = a2.id)
|
831
|
+
JOIN Airport.csv a1 ON (R.departure = a1.id)
|
832
|
+
|
833
|
+
|
834
|
+
**17.2.3 - The `counter` method**
|
835
|
+
Adds an increment field in queries inside CTE:
|
836
|
+
> Examples:
|
837
|
+
* `R.counter('stops', 0)` # -- counter starts with 0 and increment +1
|
838
|
+
* `R2.counter('generation', 5, '- 1')` # -- for the code below...
|
839
|
+
```
|
840
|
+
R2 = Recursive.create(
|
841
|
+
'Ancestors a', 'People(id,name,father,mother,birth)',
|
842
|
+
'(% = a.father OR % = a.mother)', 32630, '.parquet'
|
843
|
+
)
|
844
|
+
```
|
845
|
+
...Results:
|
846
|
+
|
847
|
+
WITH RECURSIVE Ancestors AS (
|
848
|
+
SELECT p1.id, p1.name, p1.father, p1.mother, p1.birth,
|
849
|
+
5 AS generation /* <<---- Most current generation ------------*/
|
850
|
+
FROM People.parquet p1 WHERE p1.id = 32630
|
851
|
+
UNION ALL
|
852
|
+
SELECT p2.id, p2.name, p2.father, p2.mother, p2.birth,
|
853
|
+
(generation- 1) AS generation /* <<-- Previous generation -----*/
|
854
|
+
FROM People.parquet p2 , Ancestors a WHERE (p2.id = a.father OR p2.id = a.mother)
|
855
|
+
)SELECT * FROM Ancestors a
|
856
|
+
|
857
|
+
|
858
|
+
>> Note: Comments added later.
|
859
|
+
---
|
@@ -0,0 +1,7 @@
|
|
1
|
+
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
+
sql_blocks/sql_blocks.py,sha256=Ho1Q7yej4MoDsXC9nvtnb2nsHjAk2MNi0zkWj65uYTk,61017
|
3
|
+
sql_blocks-1.25.47.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
+
sql_blocks-1.25.47.dist-info/METADATA,sha256=lyeh3HdsFXdUcaOc3TBpP1ynv5EqhofIlOiausQef88,21261
|
5
|
+
sql_blocks-1.25.47.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
+
sql_blocks-1.25.47.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
+
sql_blocks-1.25.47.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
|
2
|
-
sql_blocks/sql_blocks.py,sha256=R2xZsaKRd600rsZf0lzyXITALpZ3ZXBmwOADNpX1qUA,45798
|
3
|
-
sql_blocks-1.25.2.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
4
|
-
sql_blocks-1.25.2.dist-info/METADATA,sha256=NKLAHw43avYevBxxWhKBts8tOB2AaKETMp08dCoSAzQ,13423
|
5
|
-
sql_blocks-1.25.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
sql_blocks-1.25.2.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
|
7
|
-
sql_blocks-1.25.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|