sql-blocks 0.0.6__tar.gz → 0.0.8__tar.gz
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-0.0.6/sql_blocks.egg-info → sql_blocks-0.0.8}/PKG-INFO +36 -3
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/README.md +35 -2
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/pyproject.toml +1 -1
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/setup.py +1 -1
- sql_blocks-0.0.8/sql_blocks/__init__.py +1 -0
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/sql_blocks/sql_blocks.py +143 -35
- {sql_blocks-0.0.6 → sql_blocks-0.0.8/sql_blocks.egg-info}/PKG-INFO +36 -3
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/sql_blocks.egg-info/SOURCES.txt +1 -2
- sql_blocks-0.0.6/sql_blocks/__init__.py +0 -1
- sql_blocks-0.0.6/tests/tests.py +0 -110
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/LICENSE +0 -0
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/setup.cfg +0 -0
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/sql_blocks.egg-info/dependency_links.txt +0 -0
- {sql_blocks-0.0.6 → sql_blocks-0.0.8}/sql_blocks.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.8
|
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
|
@@ -306,10 +306,43 @@ m2 = Select(
|
|
306
306
|
Select(
|
307
307
|
'Product',
|
308
308
|
label=Case('price').when(
|
309
|
-
|
309
|
+
lt(50), 'cheap'
|
310
310
|
).when(
|
311
|
-
|
311
|
+
gt(100), 'expensive'
|
312
312
|
).else_value(
|
313
313
|
'normal'
|
314
314
|
)
|
315
315
|
)
|
316
|
+
|
317
|
+
---
|
318
|
+
|
319
|
+
### 11 - optimize method
|
320
|
+
p1 = Select.parse("""
|
321
|
+
SELECT * FROM Product p
|
322
|
+
WHERE (p.category = 'Gizmo'
|
323
|
+
OR p.category = 'Gadget'
|
324
|
+
OR p.category = 'Doohickey')
|
325
|
+
AND NOT price <= 387.64
|
326
|
+
AND YEAR(last_sale) = 2024
|
327
|
+
ORDER BY
|
328
|
+
category
|
329
|
+
""")[0]
|
330
|
+
p1.optimize() # <<===============
|
331
|
+
p2 = Select.parse("""
|
332
|
+
SELECT category FROM Product p
|
333
|
+
WHERE category IN ('Gizmo','Gadget','Doohickey')
|
334
|
+
and p.price > 387.64
|
335
|
+
and p.last_sale >= '2024-01-01'
|
336
|
+
and p.last_sale <= '2024-12-31'
|
337
|
+
ORDER BY p.category LIMIT 100
|
338
|
+
""")[0]
|
339
|
+
p1 == p2 # --- True!
|
340
|
+
|
341
|
+
This will...
|
342
|
+
* Replace `OR` conditions to `SELECT IN ...`
|
343
|
+
* Put `LIMIT` if no fields or conditions defined;
|
344
|
+
* Normalizes inverted conditions;
|
345
|
+
* Auto includes fields present in `ORDER/GROUP BY`;
|
346
|
+
* Replace `YEAR` function with date range comparison.
|
347
|
+
|
348
|
+
> The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
|
@@ -291,10 +291,43 @@ m2 = Select(
|
|
291
291
|
Select(
|
292
292
|
'Product',
|
293
293
|
label=Case('price').when(
|
294
|
-
|
294
|
+
lt(50), 'cheap'
|
295
295
|
).when(
|
296
|
-
|
296
|
+
gt(100), 'expensive'
|
297
297
|
).else_value(
|
298
298
|
'normal'
|
299
299
|
)
|
300
300
|
)
|
301
|
+
|
302
|
+
---
|
303
|
+
|
304
|
+
### 11 - optimize method
|
305
|
+
p1 = Select.parse("""
|
306
|
+
SELECT * FROM Product p
|
307
|
+
WHERE (p.category = 'Gizmo'
|
308
|
+
OR p.category = 'Gadget'
|
309
|
+
OR p.category = 'Doohickey')
|
310
|
+
AND NOT price <= 387.64
|
311
|
+
AND YEAR(last_sale) = 2024
|
312
|
+
ORDER BY
|
313
|
+
category
|
314
|
+
""")[0]
|
315
|
+
p1.optimize() # <<===============
|
316
|
+
p2 = Select.parse("""
|
317
|
+
SELECT category FROM Product p
|
318
|
+
WHERE category IN ('Gizmo','Gadget','Doohickey')
|
319
|
+
and p.price > 387.64
|
320
|
+
and p.last_sale >= '2024-01-01'
|
321
|
+
and p.last_sale <= '2024-12-31'
|
322
|
+
ORDER BY p.category LIMIT 100
|
323
|
+
""")[0]
|
324
|
+
p1 == p2 # --- True!
|
325
|
+
|
326
|
+
This will...
|
327
|
+
* Replace `OR` conditions to `SELECT IN ...`
|
328
|
+
* Put `LIMIT` if no fields or conditions defined;
|
329
|
+
* Normalizes inverted conditions;
|
330
|
+
* Auto includes fields present in `ORDER/GROUP BY`;
|
331
|
+
* Replace `YEAR` function with date range comparison.
|
332
|
+
|
333
|
+
> The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
|
@@ -0,0 +1 @@
|
|
1
|
+
from sql_blocks import *
|
@@ -10,26 +10,32 @@ DISTINCT_SF_PR = f'(DISTINCT|distinct)|{SUFFIX_AND_PRE}'
|
|
10
10
|
KEYWORD = {
|
11
11
|
'SELECT': (',{}', 'SELECT *', DISTINCT_SF_PR),
|
12
12
|
'FROM': ('{}', '', PATTERN_SUFFIX),
|
13
|
-
'WHERE': ('{}AND ', '',
|
13
|
+
'WHERE': ('{}AND ', '', ''),
|
14
14
|
'GROUP BY': (',{}', '', SUFFIX_AND_PRE),
|
15
15
|
'ORDER BY': (',{}', '', SUFFIX_AND_PRE),
|
16
16
|
'LIMIT': (' ', '', ''),
|
17
17
|
}
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
18
|
+
# ^ ^ ^
|
19
|
+
# | | |
|
20
|
+
# | | +----- pattern to compare fields
|
21
|
+
# | |
|
22
|
+
# | +----- default when empty (SELECT * ...)
|
23
|
+
# |
|
24
|
+
# +-------- separator
|
25
25
|
|
26
26
|
SELECT, FROM, WHERE, GROUP_BY, ORDER_BY, LIMIT = KEYWORD.keys()
|
27
|
-
USUAL_KEYS = [SELECT, WHERE, GROUP_BY, ORDER_BY]
|
27
|
+
USUAL_KEYS = [SELECT, WHERE, GROUP_BY, ORDER_BY, LIMIT]
|
28
28
|
|
29
29
|
|
30
30
|
class SQLObject:
|
31
|
+
ALIAS_FUNC = lambda t: t.lower()[:3]
|
32
|
+
""" ^^^^^^^^^^^^^^^^^^^^^^^^
|
33
|
+
You can change the behavior by assigning
|
34
|
+
a user function to SQLObject.ALIAS_FUNC
|
35
|
+
"""
|
36
|
+
|
31
37
|
def __init__(self, table_name: str=''):
|
32
|
-
self.
|
38
|
+
self.__alias = ''
|
33
39
|
self.values = {}
|
34
40
|
self.key_field = ''
|
35
41
|
self.set_table(table_name)
|
@@ -37,20 +43,26 @@ class SQLObject:
|
|
37
43
|
def set_table(self, table_name: str):
|
38
44
|
if not table_name:
|
39
45
|
return
|
40
|
-
if ' ' in table_name:
|
41
|
-
table_name, self.
|
46
|
+
if ' ' in table_name.strip():
|
47
|
+
table_name, self.__alias = table_name.split()
|
42
48
|
elif '_' in table_name:
|
43
|
-
self.
|
49
|
+
self.__alias = ''.join(
|
44
50
|
word[0].lower()
|
45
51
|
for word in table_name.split('_')
|
46
52
|
)
|
47
53
|
else:
|
48
|
-
self.
|
54
|
+
self.__alias = SQLObject.ALIAS_FUNC(table_name)
|
49
55
|
self.values.setdefault(FROM, []).append(f'{table_name} {self.alias}')
|
50
56
|
|
51
57
|
@property
|
52
58
|
def table_name(self) -> str:
|
53
59
|
return self.values[FROM][0].split()[0]
|
60
|
+
|
61
|
+
@property
|
62
|
+
def alias(self) -> str:
|
63
|
+
if self.__alias:
|
64
|
+
return self.__alias
|
65
|
+
return self.table_name
|
54
66
|
|
55
67
|
@staticmethod
|
56
68
|
def get_separator(key: str) -> str:
|
@@ -58,11 +70,17 @@ class SQLObject:
|
|
58
70
|
return KEYWORD[key][0].format(appendix.get(key, ''))
|
59
71
|
|
60
72
|
def diff(self, key: str, search_list: list, symmetrical: bool=False) -> set:
|
61
|
-
|
73
|
+
def cleanup(fld: str) -> str:
|
74
|
+
if symmetrical:
|
75
|
+
fld = fld.lower()
|
76
|
+
return fld.strip()
|
77
|
+
pattern = KEYWORD[key][2]
|
78
|
+
if key == WHERE and symmetrical:
|
79
|
+
pattern = f'{PATTERN_PREFIX}| '
|
62
80
|
separator = self.get_separator(key)
|
63
81
|
def field_set(source: list) -> set:
|
64
82
|
return set(
|
65
|
-
re.sub(pattern, '', fld
|
83
|
+
re.sub(pattern, '', cleanup(fld))
|
66
84
|
for string in source
|
67
85
|
for fld in re.split(separator, string)
|
68
86
|
)
|
@@ -90,9 +108,9 @@ class Field:
|
|
90
108
|
@classmethod
|
91
109
|
def format(cls, name: str, main: SQLObject) -> str:
|
92
110
|
name = name.strip()
|
93
|
-
if name
|
111
|
+
if name in ('_', '*'):
|
94
112
|
name = '*'
|
95
|
-
elif '.'
|
113
|
+
elif not re.findall('[.()0-9]', name):
|
96
114
|
name = f'{main.alias}.{name}'
|
97
115
|
if Function in cls.__bases__:
|
98
116
|
name = f'{cls.__name__}({name})'
|
@@ -185,7 +203,7 @@ class Where:
|
|
185
203
|
prefix = ''
|
186
204
|
|
187
205
|
def __init__(self, expr: str):
|
188
|
-
self.expr =
|
206
|
+
self.expr = expr
|
189
207
|
|
190
208
|
@classmethod
|
191
209
|
def __constructor(cls, operator: str, value):
|
@@ -226,8 +244,8 @@ class Where:
|
|
226
244
|
return cls(f'IN ({values})')
|
227
245
|
|
228
246
|
def add(self, name: str, main: SQLObject):
|
229
|
-
main.values.setdefault(WHERE, []).append('{} {}'.format(
|
230
|
-
Field.format(name, main), self.expr
|
247
|
+
main.values.setdefault(WHERE, []).append('{}{} {}'.format(
|
248
|
+
self.prefix, Field.format(name, main), self.expr
|
231
249
|
))
|
232
250
|
|
233
251
|
|
@@ -347,6 +365,12 @@ class Having:
|
|
347
365
|
return cls(Count, condition)
|
348
366
|
|
349
367
|
|
368
|
+
class Rule:
|
369
|
+
@classmethod
|
370
|
+
def apply(cls, target: 'Select'):
|
371
|
+
...
|
372
|
+
|
373
|
+
|
350
374
|
class JoinType(Enum):
|
351
375
|
INNER = ''
|
352
376
|
LEFT = 'LEFT '
|
@@ -367,16 +391,17 @@ class Select(SQLObject):
|
|
367
391
|
self.values.setdefault(key, []).append(value)
|
368
392
|
|
369
393
|
def add(self, name: str, main: SQLObject):
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
394
|
+
new_tables = [
|
395
|
+
'{jt}JOIN {tb} {a2} ON ({a1}.{f1} = {a2}.{f2})'.format(
|
396
|
+
jt=self.join_type.value,
|
397
|
+
tb=self.table_name,
|
398
|
+
a1=main.alias, f1=name,
|
399
|
+
a2=self.alias, f2=self.key_field
|
400
|
+
)
|
401
|
+
]
|
402
|
+
if new_tables not in main.values.get(FROM, []):
|
403
|
+
new_tables += self.values[FROM][1:]
|
404
|
+
main.values.setdefault(FROM, []).extend(new_tables)
|
380
405
|
for key in USUAL_KEYS:
|
381
406
|
main.update_values(key, self.values.get(key, []))
|
382
407
|
|
@@ -424,13 +449,16 @@ class Select(SQLObject):
|
|
424
449
|
return False
|
425
450
|
return True
|
426
451
|
|
427
|
-
def limit(self, row_count: int, offset: int=0):
|
452
|
+
def limit(self, row_count: int=100, offset: int=0):
|
428
453
|
result = [str(row_count)]
|
429
454
|
if offset > 0:
|
430
455
|
result.append(f'OFFSET {offset}')
|
431
456
|
self.values.setdefault(LIMIT, result)
|
432
457
|
return self
|
433
458
|
|
459
|
+
def match(self, expr: str) -> bool:
|
460
|
+
return re.findall(f'\b*{self.alias}[.]', expr) != []
|
461
|
+
|
434
462
|
@classmethod
|
435
463
|
def parse(cls, txt: str) -> list[SQLObject]:
|
436
464
|
def find_last_word(pos: int) -> int:
|
@@ -452,7 +480,7 @@ class Select(SQLObject):
|
|
452
480
|
if not cls.REGEX:
|
453
481
|
keywords = '|'.join(k + r'\b' for k in KEYWORD)
|
454
482
|
flags = re.IGNORECASE + re.MULTILINE
|
455
|
-
cls.REGEX['keywords'] = re.compile(f'({keywords})', flags)
|
483
|
+
cls.REGEX['keywords'] = re.compile(f'({keywords}|[*])', flags)
|
456
484
|
cls.REGEX['subquery'] = re.compile(r'(\w\.)*\w+ +in +\(SELECT.*?\)', flags)
|
457
485
|
result = {}
|
458
486
|
found = cls.REGEX['subquery'].search(txt)
|
@@ -476,7 +504,7 @@ class Select(SQLObject):
|
|
476
504
|
result[obj.alias] = obj
|
477
505
|
txt = txt[:start-1] + txt[end+1:]
|
478
506
|
found = cls.REGEX['subquery'].search(txt)
|
479
|
-
tokens = [t.strip() for t in cls.REGEX['keywords'].split(txt) if
|
507
|
+
tokens = [t.strip() for t in cls.REGEX['keywords'].split(txt) if t.strip()]
|
480
508
|
values = {k.upper(): v for k, v in zip(tokens[::2], tokens[1::2])}
|
481
509
|
tables = [t.strip() for t in re.split('JOIN|LEFT|RIGHT|ON', values[FROM]) if t.strip()]
|
482
510
|
for item in tables:
|
@@ -495,11 +523,18 @@ class Select(SQLObject):
|
|
495
523
|
obj.values[key] = [
|
496
524
|
Field.format(fld, obj)
|
497
525
|
for fld in re.split(separator, values[key])
|
498
|
-
if len(tables) == 1 or
|
526
|
+
if (fld != '*' and len(tables) == 1) or obj.match(fld)
|
499
527
|
]
|
500
528
|
result[obj.alias] = obj
|
501
529
|
return list( result.values() )
|
502
530
|
|
531
|
+
def optimize(self, rules: list[Rule]=None):
|
532
|
+
if not rules:
|
533
|
+
rules = Rule.__subclasses__()
|
534
|
+
for rule in rules:
|
535
|
+
rule.apply(self)
|
536
|
+
|
537
|
+
|
503
538
|
class SelectIN(Select):
|
504
539
|
condition_class = Where
|
505
540
|
|
@@ -511,3 +546,76 @@ SubSelect = SelectIN
|
|
511
546
|
|
512
547
|
class NotSelectIN(SelectIN):
|
513
548
|
condition_class = Not
|
549
|
+
|
550
|
+
|
551
|
+
class RulePutLimit(Rule):
|
552
|
+
@classmethod
|
553
|
+
def apply(cls, target: Select):
|
554
|
+
need_limit = any(not target.values.get(key) for key in (WHERE, SELECT))
|
555
|
+
if need_limit:
|
556
|
+
target.limit()
|
557
|
+
|
558
|
+
class RuleSelectIN(Rule):
|
559
|
+
@classmethod
|
560
|
+
def apply(cls, target: Select):
|
561
|
+
for i, condition in enumerate(target.values[WHERE]):
|
562
|
+
tokens = re.split(' or | OR ', re.sub('\n|\t|[()]', ' ', condition))
|
563
|
+
if len(tokens) < 2:
|
564
|
+
continue
|
565
|
+
fields = [t.split('=')[0].split('.')[-1].lower().strip() for t in tokens]
|
566
|
+
if len(set(fields)) == 1:
|
567
|
+
target.values[WHERE][i] = '{} IN ({})'.format(
|
568
|
+
Field.format(fields[0], target),
|
569
|
+
','.join(t.split('=')[-1].strip() for t in tokens)
|
570
|
+
)
|
571
|
+
|
572
|
+
class RuleAutoField(Rule):
|
573
|
+
@classmethod
|
574
|
+
def apply(cls, target: Select):
|
575
|
+
if target.values.get(GROUP_BY):
|
576
|
+
target.values[SELECT] = target.values[GROUP_BY]
|
577
|
+
target.values[ORDER_BY] = []
|
578
|
+
elif target.values.get(ORDER_BY):
|
579
|
+
s1 = set(target.values.get(SELECT, []))
|
580
|
+
s2 = set(target.values[ORDER_BY])
|
581
|
+
target.values.setdefault(SELECT, []).extend( list(s2-s1) )
|
582
|
+
|
583
|
+
class RuleLogicalOp(Rule):
|
584
|
+
REVERSE = {
|
585
|
+
">=": "<",
|
586
|
+
"<=": ">",
|
587
|
+
"<>": "=",
|
588
|
+
"=": "<>"
|
589
|
+
}
|
590
|
+
@classmethod
|
591
|
+
def apply(cls, target: Select):
|
592
|
+
REGEX = re.compile('({})'.format(
|
593
|
+
'|'.join(cls.REVERSE)
|
594
|
+
))
|
595
|
+
for i, condition in enumerate(target.values.get(WHERE, [])):
|
596
|
+
expr = re.sub('\n|\t', ' ', condition)
|
597
|
+
tokens = [t for t in re.split(r'(NOT\b|not\b)',expr) if t.strip()]
|
598
|
+
if len(tokens) < 2 or not REGEX.findall(tokens[-1]):
|
599
|
+
continue
|
600
|
+
tokens = REGEX.split(tokens[-1])
|
601
|
+
tokens[1] = cls.REVERSE[tokens[1]]
|
602
|
+
target.values[WHERE][i] = ' '.join(tokens)
|
603
|
+
|
604
|
+
class RuleDateFuncReplace(Rule):
|
605
|
+
"""
|
606
|
+
SQL algorithm by Ralff Matias
|
607
|
+
"""
|
608
|
+
REGEX = re.compile(r'(\bYEAR[(]|\byear[(]|=|[)])')
|
609
|
+
|
610
|
+
@classmethod
|
611
|
+
def apply(cls, target: Select):
|
612
|
+
for i, condition in enumerate(target.values.get(WHERE, [])):
|
613
|
+
tokens = [
|
614
|
+
t.strip() for t in cls.REGEX.split(condition) if t.strip()
|
615
|
+
]
|
616
|
+
if len(tokens) < 3:
|
617
|
+
continue
|
618
|
+
func, field, *rest, year = tokens
|
619
|
+
temp = Select(f'{target.table_name} {target.alias}')
|
620
|
+
Between(f'{year}-01-01', f'{year}-12-31').add(field, temp)
|
621
|
+
target.values[WHERE][i] = ' AND '.join(temp.values[WHERE])
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.8
|
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
|
@@ -306,10 +306,43 @@ m2 = Select(
|
|
306
306
|
Select(
|
307
307
|
'Product',
|
308
308
|
label=Case('price').when(
|
309
|
-
|
309
|
+
lt(50), 'cheap'
|
310
310
|
).when(
|
311
|
-
|
311
|
+
gt(100), 'expensive'
|
312
312
|
).else_value(
|
313
313
|
'normal'
|
314
314
|
)
|
315
315
|
)
|
316
|
+
|
317
|
+
---
|
318
|
+
|
319
|
+
### 11 - optimize method
|
320
|
+
p1 = Select.parse("""
|
321
|
+
SELECT * FROM Product p
|
322
|
+
WHERE (p.category = 'Gizmo'
|
323
|
+
OR p.category = 'Gadget'
|
324
|
+
OR p.category = 'Doohickey')
|
325
|
+
AND NOT price <= 387.64
|
326
|
+
AND YEAR(last_sale) = 2024
|
327
|
+
ORDER BY
|
328
|
+
category
|
329
|
+
""")[0]
|
330
|
+
p1.optimize() # <<===============
|
331
|
+
p2 = Select.parse("""
|
332
|
+
SELECT category FROM Product p
|
333
|
+
WHERE category IN ('Gizmo','Gadget','Doohickey')
|
334
|
+
and p.price > 387.64
|
335
|
+
and p.last_sale >= '2024-01-01'
|
336
|
+
and p.last_sale <= '2024-12-31'
|
337
|
+
ORDER BY p.category LIMIT 100
|
338
|
+
""")[0]
|
339
|
+
p1 == p2 # --- True!
|
340
|
+
|
341
|
+
This will...
|
342
|
+
* Replace `OR` conditions to `SELECT IN ...`
|
343
|
+
* Put `LIMIT` if no fields or conditions defined;
|
344
|
+
* Normalizes inverted conditions;
|
345
|
+
* Auto includes fields present in `ORDER/GROUP BY`;
|
346
|
+
* Replace `YEAR` function with date range comparison.
|
347
|
+
|
348
|
+
> The method allows you to select which rules you want to apply in the optimization...Or define your own rules!
|
@@ -1 +0,0 @@
|
|
1
|
-
from sql_blocks.sql_blocks import *
|
sql_blocks-0.0.6/tests/tests.py
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
from sql_blocks.sql_blocks import *
|
2
|
-
|
3
|
-
|
4
|
-
Select.join_type = JoinType.LEFT
|
5
|
-
OrderBy.sort = SortType.DESC
|
6
|
-
|
7
|
-
def best_movies() -> SelectIN:
|
8
|
-
return SelectIN(
|
9
|
-
'Review r', movie=[GroupBy, Distinct], rate=Having.avg(Where.gt(4.5))
|
10
|
-
)
|
11
|
-
|
12
|
-
def detached_objects() -> tuple:
|
13
|
-
def select_actor() -> Select:
|
14
|
-
return Select('Actor a', cast=ForeignKey('Cast'),
|
15
|
-
name=NamedField('actors_name'), age=Between(45, 69)
|
16
|
-
)
|
17
|
-
def select_cast() -> Select:
|
18
|
-
return Select(
|
19
|
-
Cast=Table('role'), id=PrimaryKey, movie=ForeignKey('Movie'),
|
20
|
-
)
|
21
|
-
def select_movie() -> Select:
|
22
|
-
return Select('Movie m', title=Field,
|
23
|
-
release_date=[OrderBy, Field], id=PrimaryKey,
|
24
|
-
OR=Options(
|
25
|
-
genre=Where.eq('Sci-Fi'), awards=Where.like('Oscar')
|
26
|
-
), director=[Where.like('Coppola'), Field, OrderBy]
|
27
|
-
)
|
28
|
-
return select_actor(), select_cast(), select_movie()
|
29
|
-
|
30
|
-
def query_reference() -> Select:
|
31
|
-
return Select('Actor a', age=Between(45, 69),
|
32
|
-
cast=Select(
|
33
|
-
Cast=Table('role'), id=PrimaryKey,
|
34
|
-
movie=Select(
|
35
|
-
'Movie m', title=Field,
|
36
|
-
release_date=[OrderBy, Field],
|
37
|
-
id=[
|
38
|
-
SelectIN(
|
39
|
-
'Review r', movie=[GroupBy, Distinct],
|
40
|
-
rate=Having.avg(Where.gt(4.5))
|
41
|
-
),
|
42
|
-
PrimaryKey
|
43
|
-
], OR=Options(
|
44
|
-
genre=Where.eq('Sci-Fi'), awards=Where.like('Oscar')
|
45
|
-
)
|
46
|
-
) # --- Movie
|
47
|
-
), # ------- Cast
|
48
|
-
name=NamedField('actors_name'),
|
49
|
-
) # ----------- Actor
|
50
|
-
|
51
|
-
SINGLE_CONDITION_GENRE = "( m.genre = 'Sci-Fi' OR m.awards LIKE '%Oscar%' )"
|
52
|
-
SUB_QUERIES_CONDITIONS = """
|
53
|
-
m.genre NOT in (SELECT g.id from Genres g where g.name in ('sci-fi', 'horror', 'distopia'))
|
54
|
-
AND (m.hashtag = '#cult' OR m.awards LIKE '%Oscar%')
|
55
|
-
AND m.id IN (select DISTINCT r.movie FROM Review r GROUP BY r.movie HAVING Avg(r.rate) > 4.5)
|
56
|
-
"""
|
57
|
-
|
58
|
-
def single_text_to_objects(conditions: str=SINGLE_CONDITION_GENRE):
|
59
|
-
return Select.parse(f'''
|
60
|
-
SELECT
|
61
|
-
cas.role,
|
62
|
-
m.title,
|
63
|
-
m.release_date,
|
64
|
-
a.name as actors_name
|
65
|
-
FROM
|
66
|
-
Actor a
|
67
|
-
LEFT JOIN Cast cas ON (a.cast = cas.id)
|
68
|
-
LEFT JOIN Movie m ON (cas.movie = m.id)
|
69
|
-
WHERE
|
70
|
-
{conditions}
|
71
|
-
AND a.age <= 69 AND a.age >= 45
|
72
|
-
ORDER BY
|
73
|
-
m.release_date DESC
|
74
|
-
''')
|
75
|
-
|
76
|
-
def many_texts_to_objects():
|
77
|
-
ForeignKey.references = {
|
78
|
-
('Actor', 'Cast'): ('cast', 'id'),
|
79
|
-
('Cast', 'Movie'): ('movie', 'id'),
|
80
|
-
}
|
81
|
-
actor = Select.parse('''
|
82
|
-
SELECT name as actors_name FROM Actor a
|
83
|
-
WHERE a.age >= 45 AND a.age <= 69
|
84
|
-
''')[0]
|
85
|
-
cast = Select.parse('SELECT role FROM Cast')[0]
|
86
|
-
movie = Select.parse("""
|
87
|
-
SELECT title, release_date FROM Movie m ORDER BY release_date DESC
|
88
|
-
WHERE ( m.genre = 'Sci-Fi' OR m.awards LIKE '%Oscar%' ) GROUP BY director
|
89
|
-
""")[0]
|
90
|
-
return actor, cast, movie
|
91
|
-
|
92
|
-
def two_queries_same_table() -> Select:
|
93
|
-
txt1 = """SELECT p.name, p.category
|
94
|
-
,p.price,p.promotional FROM product p
|
95
|
-
where p.category in (6,14,29,35,78)
|
96
|
-
AND p.Status = p.last_st ORDER BY p.EAN"""
|
97
|
-
txt2 = """select stock_amount, EAN,Name ,expiration_date
|
98
|
-
from PRODUCT where price < 357.46 and status = Last_ST order by ean"""
|
99
|
-
return Select.parse(txt1)[0] + Select.parse(txt2)[0]
|
100
|
-
|
101
|
-
def select_product() -> Select:
|
102
|
-
return Select(
|
103
|
-
Product=Table('name,promotional,stock_amount,expiration_date'),
|
104
|
-
category=[Where.list([6,14,29,35,78]),Field], EAN=[Field, OrderBy],
|
105
|
-
price=[Where.lt(357.46),Field], status=Where('= Last_st')
|
106
|
-
)
|
107
|
-
|
108
|
-
def extract_subqueries() -> dict:
|
109
|
-
query_list = single_text_to_objects(SUB_QUERIES_CONDITIONS)
|
110
|
-
return {query.table_name: query for query in query_list}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|