sql-blocks 1.25.113__py3-none-any.whl → 1.25.514__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 1.25.113
3
+ Version: 1.25.514
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
@@ -15,6 +15,10 @@ License-File: LICENSE
15
15
 
16
16
  # SQL_Blocks
17
17
 
18
+ ## _SQL_Blocks_ is useful for building complex SQL commands through smaller query blocks:
19
+
20
+ ---
21
+
18
22
  ### 1 - You can assemble a simple object that will then be converted into an SQL command:
19
23
 
20
24
  > a = Select('Actor') # --> SELECT * FROM Actor act
@@ -52,6 +56,12 @@ You can specify your own alias: `a = Select('Actor a')`
52
56
  ...should return:
53
57
  **SELECT extract(year from due_date) as YEAR_ref...**
54
58
 
59
+ Possible tags in ExpressionField:
60
+ * {f} - The field name;
61
+ * {af} - The field name preceded by the table alias;
62
+ > Can be written as {a.f} or %
63
+ * {t} - The table name;
64
+ * {a} - Only the table alias.
55
65
  ---
56
66
 
57
67
  ### 3 - To set conditions, use **Where**:
@@ -67,6 +77,16 @@ You can specify your own alias: `a = Select('Actor a')`
67
77
  3.1 -- If you want to filter the field on a range of values:
68
78
 
69
79
  `a = Select( 'Actor a', age=Between(45, 69) )`
80
+ ...but if it is a time slot within the same day, you can do it like this:
81
+ `Select(..., event_date=SameDay("2024-10-03"))`
82
+ This results in
83
+ ```
84
+ SELECT ...
85
+ WHERE
86
+ event_date >= '2024-10-03 00:00:00' AND
87
+ event_date <= '2024-10-03 23:59:59'
88
+ ```
89
+ ---
70
90
 
71
91
  3.2 -- Sub-queries:
72
92
  ```
@@ -98,10 +118,7 @@ query = Select('Movie m', title=Field,
98
118
  awards=contains("Oscar")
99
119
  )
100
120
  AND=Options(
101
- ..., name=contains(
102
- 'Chris',
103
- Position.StartsWith
104
- )
121
+ ..., name=startswith('Chris')
105
122
  )
106
123
  ```
107
124
 
@@ -115,6 +132,16 @@ based_on_book=Not.is_null()
115
132
  hash_tag=inside(['space', 'monster', 'gore'])
116
133
  ```
117
134
 
135
+ 3.6 -- Combining ExpressionField with Where condition:
136
+ * The **formula** method allows you to write an expression as a condition:
137
+ ```
138
+ query=Select(
139
+ 'Folks f2',
140
+ id=Where.formula('({af} = a.father OR {af} = a.mother)')
141
+ )
142
+ ```
143
+ > Results: `WHERE...f2.id = a.father OR f2.id = a.mother`
144
+
118
145
  ---
119
146
  ### 4 - A field can be two things at the same time:
120
147
 
@@ -143,6 +170,35 @@ FROM
143
170
  JOIN Cast c ON (a.cast = c.id)
144
171
  ```
145
172
 
173
+ ---
174
+ **5.1 Multiple tables without JOIN**
175
+ > Warning: This is **NOT** recommended! ⛔
176
+
177
+
178
+ #### Example:
179
+ singer = Select(
180
+ "Singer artist", id=PrimaryKey,
181
+ name=NamedField('artist_name')
182
+ )
183
+ album = Select (
184
+ "Album album",
185
+ name=NamedField('album_name'),
186
+ artist_id=Where.join(singer), # <===== 👀
187
+ )
188
+ **>> print(query)**
189
+
190
+ SELECT
191
+ album.name as album_name,
192
+ artist.name as artist_name,
193
+ album.year_recorded
194
+ FROM
195
+ Album album
196
+ ,Singer artist
197
+ WHERE
198
+ (album.artist_id = artist.id)
199
+
200
+
201
+
146
202
  ---
147
203
  ### 6 - The reverse process (parse):
148
204
  ```
@@ -263,6 +319,30 @@ m = Select...
263
319
  >> **m + c => Ok!**
264
320
 
265
321
 
322
+ ---
323
+ **8.3 Difference between queries**
324
+ ```
325
+ STATUS_DELIVERED_OK = 93
326
+ orders = Select('orders',
327
+ customer_id=ForeignKey('customers'),
328
+ status=eq(STATUS_DELIVERED_OK)
329
+ )
330
+ customers = Select('customers'
331
+ id=PrimaryKey, name=Field
332
+ )
333
+ gap = orders - customers
334
+ ```
335
+ return _customers without orders_:
336
+
337
+ SELECT
338
+ c.name
339
+ FROM
340
+ customers c
341
+ WHERE
342
+ NOT c.id IN (
343
+ SELECT o.customer_id FROM orders o
344
+ WHERE o.status = 93
345
+ )
266
346
  ---
267
347
 
268
348
  ### 9 - Comparing objects
@@ -335,6 +415,31 @@ m2 = Select(
335
415
  )
336
416
  )
337
417
 
418
+ 10.1 - If the labels used in the CASE are based on ranges of values ​​in sequence, you can use the **Range class**:
419
+
420
+ query = Select(
421
+ 'People p',
422
+ age_group=Range('age',{ # <<----------
423
+ 'adult': 50,
424
+ 'teenager': 17,
425
+ 'child': 10,
426
+ 'elderly': 70,
427
+ 'young': 21,
428
+ })
429
+ )
430
+ is equivalent to...
431
+ ```
432
+ SELECT
433
+ CASE
434
+ WHEN p.age BETWEEN 0 AND 10 THEN 'child'
435
+ WHEN p.age BETWEEN 11 AND 17 THEN 'teenager'
436
+ WHEN p.age BETWEEN 18 AND 21 THEN 'young'
437
+ WHEN p.age BETWEEN 22 AND 50 THEN 'adult'
438
+ WHEN p.age BETWEEN 51 AND 70 THEN 'elderly'
439
+ END AS age_group
440
+ FROM
441
+ People p
442
+ ```
338
443
  ---
339
444
 
340
445
  ### 11 - optimize method
@@ -373,7 +478,7 @@ m2 = Select(
373
478
  query = Select(
374
479
  'Installments i', due_date=Field, customer=Select(
375
480
  'Customer c', id=PrimaryKey,
376
- name=contains('Smith', Position.EndsWith)
481
+ name=endswith('Smith')
377
482
  )
378
483
  )
379
484
  print(query)
@@ -404,7 +509,7 @@ WHERE
404
509
  query = Select('post p')
405
510
  query.add_fields(
406
511
  'user_id, created_at',
407
- order_by=True, group_by=True
512
+ [OrderBy, GroupBy]
408
513
  )
409
514
  ```
410
515
  ...is the same as...
@@ -453,6 +558,7 @@ ORDER BY
453
558
  * `^` Put the field in the ORDER BY clause
454
559
  * `@` Immediately after the table name, it indicates the grouping field.
455
560
  * `$` For SQL functions like **avg**$_field_, **sum**$_field_, **count**$_field_...
561
+ * `*` Sets the primary key field.
456
562
 
457
563
 
458
564
  ---
@@ -554,12 +660,18 @@ It consists of the inverse process of parsing: From a Select object, it returns
554
660
  ---
555
661
  ### 14 - Window Function
556
662
 
557
- Aggregation functions (Avg, Min, Max, Sum, Count) have the **over** method...
663
+ Aggregation functions (Avg, Min, Max, Sum, Count) -- or Window functions (Lead, Lag, Row_Number, Rank) -- have the **over** method...
558
664
 
559
665
  query=Select(
560
666
  'Enrollment e',
561
667
  payment=Sum().over(
562
- partition='student_id', order='due_date'
668
+ student_id=Partition, due_date=OrderBy,
669
+ # _=Rows(Current(), Following(5)),
670
+ # ^^^-------> ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING
671
+ # _=Rows(Preceding(3), Following()),
672
+ # ^^^-------> ROWS BETWEEN 3 PRECEDING AND UNBOUNDED FOLLOWING
673
+ # _=Rows(Preceding(3))
674
+ # ^^^-------> ROWS 3 PRECEDING
563
675
  ).As('sum_per_student')
564
676
  )
565
677
 
@@ -625,3 +737,152 @@ For example, if your query is going to run on Oracle, do the following:
625
737
 
626
738
  `Function.dialect = Dialect.ORACLE`
627
739
 
740
+
741
+ > Most of this functions you can use nested inside each other.
742
+ *Example:*
743
+ ```
744
+ Select(...
745
+ event_date=Substring(
746
+ Cast("CHAR"), 12, 19
747
+ ).As('time')
748
+ )
749
+ ```
750
+ Results...
751
+ ```
752
+ SELECT ...
753
+ SubString(Cast(event_date As char), 12, 19) as time
754
+ ```
755
+
756
+ >> `Function.auto_convert` option (default: True)
757
+
758
+ - Put Cast(...) when there is a difference between the types of the parameter and the return of the nested function
759
+ ```
760
+ birth=Round( DateDiff(Current_Date()) ).As('age')
761
+ ```
762
+ ...Returns...
763
+ ```
764
+ SELECT
765
+ Round(
766
+ Cast(Current_Date() - p.birth As FLOAT)
767
+ /* ^^^ */
768
+ ) as age
769
+ ...
770
+ ```
771
+ ---
772
+
773
+ ### 17 - CTE and Recursive classes
774
+
775
+ * **17.1 - _CTE class_**
776
+ ```
777
+ query = Select(
778
+ 'SocialMedia s', post=Count, reaction=Sum, user=GroupBy
779
+ )
780
+ print( CTE('Metrics', [query]) )
781
+ ```
782
+ The result is...
783
+ ```
784
+ WITH Metrics AS (
785
+ SELECT Count(s.post), Sum(s.reaction) FROM SocialMedia s GROUP BY user
786
+ )SELECT * FROM Metrics
787
+ ```
788
+
789
+ * **17.2 - _Recursive class_**
790
+ ```
791
+ q1 = Select(
792
+ 'SocialMedia me', name=[ eq(MY_NAME), Field ]
793
+ )
794
+ q2 = Select(
795
+ 'SocialMedia you' name=Field, id=Where.formula('{af} = n.friend')
796
+ )
797
+ print( Recursive('Network', [q1, q2]) )
798
+ ```
799
+ The result is...
800
+ ```
801
+ WITH RECURSIVE Network AS (
802
+ SELECT me.name FROM SocialMedia me WHERE
803
+ me.name = 'Júlio Cascalles'
804
+ UNION ALL
805
+ SELECT you.name FROM SocialMedia you , Network n
806
+ WHERE you.id = n.friend
807
+ )SELECT * FROM Network
808
+ ```
809
+
810
+ * **17.2.1 - The `create` method** ... parameters :
811
+ - name: The name of the CTE
812
+ - pattern: A cypher script that defines the tables used
813
+ - formula: The format for `Where.formula` method _(*)_
814
+ - init_value: The value for the condition in the first table
815
+ - format (optional): If tables are files or internet hiperlinks, you may especify the extension and/or folder...
816
+ > Example:
817
+ ```
818
+ R = Recursive.create(
819
+ 'Route R', 'Flyght(departure, arrival)',
820
+ '[2] = R.[1]', 'JFK', format='.csv'
821
+ ) # ^^^--- Flyghts from JFK airport
822
+ ```
823
+ _...Creates a recursive CTE called Route, using Flyght table, where the recursivity condition is Flyght.arrival equals to Route.departure_
824
+ >> (*) -- Note that [1] and [2] refers to first field and second field. 😉
825
+
826
+ Result:
827
+
828
+ WITH RECURSIVE Route AS (
829
+ SELECT f1.departure, f1.arrival
830
+ FROM Flyght.csv f1
831
+ WHERE f1.departure = 'JFK'
832
+ UNION ALL
833
+ SELECT f2.departure, f2.arrival
834
+ FROM Flyght.csv f2
835
+ , Route R
836
+ WHERE f2.arrival = R.departure
837
+ )SELECT * FROM Route R
838
+
839
+ **17.2.2 - The `join` method**
840
+
841
+ In the previous example, if you add this code...
842
+ `R.join('Airport(*id,name)', 'departure, arrival', format='.csv')`
843
+
844
+ ...The result would be:
845
+
846
+ WITH RECURSIVE Route AS (
847
+ SELECT f1.departure, f1.arrival
848
+ FROM Flyght.csv f1
849
+ WHERE f1.departure = 'JFK'
850
+ UNION ALL
851
+ SELECT f2.departure, f2.arrival
852
+ FROM Flyght.csv f2
853
+ , Route R
854
+ WHERE f2.arrival = R.departure
855
+ )SELECT
856
+ a1.name, a2.name
857
+ FROM
858
+ Route R
859
+ JOIN Airport.csv a2 ON (R.arrival = a2.id)
860
+ JOIN Airport.csv a1 ON (R.departure = a1.id)
861
+
862
+
863
+ **17.2.3 - The `counter` method**
864
+ Adds an increment field in queries inside CTE:
865
+ > Examples:
866
+ * `R.counter('stops', 0)` # -- counter starts with 0 and increment +1
867
+ * `R2.counter('generation', 5, '- 1')` # -- for the code below...
868
+ ```
869
+ R2 = Recursive.create(
870
+ 'Ancestors a', 'People(id,name,father,mother,birth)',
871
+ '(% = a.father OR % = a.mother)', 32630, '.parquet'
872
+ )
873
+ ```
874
+ ...Results:
875
+
876
+ WITH RECURSIVE Ancestors AS (
877
+ SELECT p1.id, p1.name, p1.father, p1.mother, p1.birth,
878
+ 5 AS generation /* <<---- Most current generation ------------*/
879
+ FROM People.parquet p1 WHERE p1.id = 32630
880
+ UNION ALL
881
+ SELECT p2.id, p2.name, p2.father, p2.mother, p2.birth,
882
+ (generation- 1) AS generation /* <<-- Previous generation -----*/
883
+ FROM People.parquet p2 , Ancestors a WHERE (p2.id = a.father OR p2.id = a.mother)
884
+ )SELECT * FROM Ancestors a
885
+
886
+
887
+ >> Note: Comments added later.
888
+ ---
@@ -0,0 +1,7 @@
1
+ sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
+ sql_blocks/sql_blocks.py,sha256=8msHsR5Ttp8vpCJbhU7wd91IP-TboC0XAc1204kLKXE,65953
3
+ sql_blocks-1.25.514.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
+ sql_blocks-1.25.514.dist-info/METADATA,sha256=qZFaDlYOrKsexm7h9cf521yuh0eomllevLjwun6q2hA,22233
5
+ sql_blocks-1.25.514.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ sql_blocks-1.25.514.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
+ sql_blocks-1.25.514.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- sql_blocks/__init__.py,sha256=5ItzGCyqqa6kwY8wvF9kapyHsAiWJ7KEXCcC-OtdXKg,37
2
- sql_blocks/sql_blocks.py,sha256=Saj3MqmF0OHY60bhc_aSl8fL-PhXM2apmiPiE6KspOc,50553
3
- sql_blocks-1.25.113.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
4
- sql_blocks-1.25.113.dist-info/METADATA,sha256=td1IZl9O3m2U0rWFk6gyecqWnkr0cMDW94PfeqhzP8Y,14638
5
- sql_blocks-1.25.113.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- sql_blocks-1.25.113.dist-info/top_level.txt,sha256=57AbUvUjYNy4m1EqDaU3WHeP-uyIAfV0n8GAUp1a1YQ,11
7
- sql_blocks-1.25.113.dist-info/RECORD,,