sql-blocks 0.0.3__tar.gz → 0.0.5__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.3/sql_blocks.egg-info → sql_blocks-0.0.5}/PKG-INFO +17 -16
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/README.md +16 -15
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/pyproject.toml +1 -1
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/setup.py +1 -1
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/sql_blocks/sql_blocks.py +141 -38
- {sql_blocks-0.0.3 → sql_blocks-0.0.5/sql_blocks.egg-info}/PKG-INFO +17 -16
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/tests/tests.py +16 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/LICENSE +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/setup.cfg +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/sql_blocks/__init__.py +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/sql_blocks.egg-info/SOURCES.txt +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/sql_blocks.egg-info/dependency_links.txt +0 -0
- {sql_blocks-0.0.3 → sql_blocks-0.0.5}/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.5
|
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
|
@@ -55,7 +55,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
55
55
|
3.2 -- Sub-queries:
|
56
56
|
```
|
57
57
|
query = Select('Movie m', title=Field,
|
58
|
-
id=
|
58
|
+
id=SelectIN(
|
59
59
|
'Review r',
|
60
60
|
rate=Where.gt(4.5),
|
61
61
|
movie_id=Distinct
|
@@ -86,12 +86,13 @@ query = Select('Movie m', title=Field,
|
|
86
86
|
|
87
87
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
88
88
|
```
|
89
|
-
franchise
|
90
89
|
based_on_book=Not.is_null()
|
91
90
|
```
|
92
91
|
|
93
92
|
3.5 -- List of values
|
93
|
+
```
|
94
94
|
hash_tag=Where.list(['space', 'monster', 'gore'])
|
95
|
+
```
|
95
96
|
|
96
97
|
---
|
97
98
|
### 4 - A field can be two things at the same time:
|
@@ -100,7 +101,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
100
101
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
101
102
|
* Applying **GROUP BY** to item 3.2, it would look like this:
|
102
103
|
```
|
103
|
-
|
104
|
+
SelectIN(
|
104
105
|
'Review r', movie=[GroupBy, Distinct],
|
105
106
|
rate=Having.avg(Where.gt(4.5))
|
106
107
|
)
|
@@ -248,13 +249,13 @@ m = Select...
|
|
248
249
|
9.1
|
249
250
|
```
|
250
251
|
a1 = Select.parse('''
|
251
|
-
SELECT gender,
|
252
|
+
SELECT gender, Max(act.age) FROM Actor act
|
252
253
|
WHERE act.age <= 69 AND act.age >= 45
|
253
254
|
GROUP BY gender
|
254
255
|
''')[0]
|
255
256
|
|
256
257
|
a2 = Select('Actor',
|
257
|
-
age=Between(45, 69),
|
258
|
+
age=[ Between(45, 69), Max ],
|
258
259
|
gender=[GroupBy, Field]
|
259
260
|
)
|
260
261
|
```
|
@@ -282,7 +283,7 @@ m = Select...
|
|
282
283
|
|
283
284
|
9.3
|
284
285
|
```
|
285
|
-
best_movies =
|
286
|
+
best_movies = SelectIN(
|
286
287
|
Review=Table('role'),
|
287
288
|
rate=[GroupBy, Having.avg(Where.gt(4.5))]
|
288
289
|
)
|
@@ -302,13 +303,13 @@ m2 = Select(
|
|
302
303
|
---
|
303
304
|
|
304
305
|
### 10 - CASE...WHEN...THEN
|
305
|
-
Select(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
306
|
+
Select(
|
307
|
+
'Product',
|
308
|
+
label=Case('price').when(
|
309
|
+
lt(50), 'cheap'
|
310
|
+
).when(
|
311
|
+
gt(100), 'expensive'
|
312
|
+
).else_value(
|
313
|
+
'normal'
|
314
|
+
)
|
313
315
|
)
|
314
|
-
)
|
@@ -40,7 +40,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
40
40
|
3.2 -- Sub-queries:
|
41
41
|
```
|
42
42
|
query = Select('Movie m', title=Field,
|
43
|
-
id=
|
43
|
+
id=SelectIN(
|
44
44
|
'Review r',
|
45
45
|
rate=Where.gt(4.5),
|
46
46
|
movie_id=Distinct
|
@@ -71,12 +71,13 @@ query = Select('Movie m', title=Field,
|
|
71
71
|
|
72
72
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
73
73
|
```
|
74
|
-
franchise
|
75
74
|
based_on_book=Not.is_null()
|
76
75
|
```
|
77
76
|
|
78
77
|
3.5 -- List of values
|
78
|
+
```
|
79
79
|
hash_tag=Where.list(['space', 'monster', 'gore'])
|
80
|
+
```
|
80
81
|
|
81
82
|
---
|
82
83
|
### 4 - A field can be two things at the same time:
|
@@ -85,7 +86,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
85
86
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
86
87
|
* Applying **GROUP BY** to item 3.2, it would look like this:
|
87
88
|
```
|
88
|
-
|
89
|
+
SelectIN(
|
89
90
|
'Review r', movie=[GroupBy, Distinct],
|
90
91
|
rate=Having.avg(Where.gt(4.5))
|
91
92
|
)
|
@@ -233,13 +234,13 @@ m = Select...
|
|
233
234
|
9.1
|
234
235
|
```
|
235
236
|
a1 = Select.parse('''
|
236
|
-
SELECT gender,
|
237
|
+
SELECT gender, Max(act.age) FROM Actor act
|
237
238
|
WHERE act.age <= 69 AND act.age >= 45
|
238
239
|
GROUP BY gender
|
239
240
|
''')[0]
|
240
241
|
|
241
242
|
a2 = Select('Actor',
|
242
|
-
age=Between(45, 69),
|
243
|
+
age=[ Between(45, 69), Max ],
|
243
244
|
gender=[GroupBy, Field]
|
244
245
|
)
|
245
246
|
```
|
@@ -267,7 +268,7 @@ m = Select...
|
|
267
268
|
|
268
269
|
9.3
|
269
270
|
```
|
270
|
-
best_movies =
|
271
|
+
best_movies = SelectIN(
|
271
272
|
Review=Table('role'),
|
272
273
|
rate=[GroupBy, Having.avg(Where.gt(4.5))]
|
273
274
|
)
|
@@ -287,13 +288,13 @@ m2 = Select(
|
|
287
288
|
---
|
288
289
|
|
289
290
|
### 10 - CASE...WHEN...THEN
|
290
|
-
Select(
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
291
|
+
Select(
|
292
|
+
'Product',
|
293
|
+
label=Case('price').when(
|
294
|
+
lt(50), 'cheap'
|
295
|
+
).when(
|
296
|
+
gt(100), 'expensive'
|
297
|
+
).else_value(
|
298
|
+
'normal'
|
299
|
+
)
|
298
300
|
)
|
299
|
-
)
|
@@ -1,13 +1,28 @@
|
|
1
1
|
from enum import Enum
|
2
|
+
import re
|
3
|
+
|
4
|
+
|
5
|
+
PATTERN_PREFIX = '([^0-9 ]+[.])'
|
6
|
+
PATTERN_SUFFIX = '( [A-Za-z_]+)'
|
7
|
+
SUFFIX_AND_PRE = f'{PATTERN_SUFFIX}|{PATTERN_PREFIX}'
|
8
|
+
DISTINCT_SF_PR = f'(DISTINCT|distinct)|{SUFFIX_AND_PRE}'
|
2
9
|
|
3
10
|
KEYWORD = {
|
4
|
-
'SELECT': (',{}', 'SELECT *'),
|
5
|
-
'FROM': ('{}', ''),
|
6
|
-
'WHERE': ('{}AND ', ''),
|
7
|
-
'GROUP BY': (',{}', ''),
|
8
|
-
'ORDER BY': (',{}', ''),
|
9
|
-
'LIMIT': (' ', ''),
|
11
|
+
'SELECT': (',{}', 'SELECT *', DISTINCT_SF_PR),
|
12
|
+
'FROM': ('{}', '', PATTERN_SUFFIX),
|
13
|
+
'WHERE': ('{}AND ', '', PATTERN_PREFIX),
|
14
|
+
'GROUP BY': (',{}', '', SUFFIX_AND_PRE),
|
15
|
+
'ORDER BY': (',{}', '', SUFFIX_AND_PRE),
|
16
|
+
'LIMIT': (' ', '', ''),
|
10
17
|
}
|
18
|
+
# ^ ^ ^
|
19
|
+
# | | |
|
20
|
+
# | | +----- pattern to compare fields
|
21
|
+
# | |
|
22
|
+
# | +----- default when empty (SELECT * ...)
|
23
|
+
# |
|
24
|
+
# +-------- separator
|
25
|
+
|
11
26
|
SELECT, FROM, WHERE, GROUP_BY, ORDER_BY, LIMIT = KEYWORD.keys()
|
12
27
|
USUAL_KEYS = [SELECT, WHERE, GROUP_BY, ORDER_BY]
|
13
28
|
|
@@ -37,6 +52,26 @@ class SQLObject:
|
|
37
52
|
def table_name(self) -> str:
|
38
53
|
return self.values[FROM][0].split()[0]
|
39
54
|
|
55
|
+
@staticmethod
|
56
|
+
def get_separator(key: str) -> str:
|
57
|
+
appendix = {WHERE: 'and|', FROM: 'join|JOIN'}
|
58
|
+
return KEYWORD[key][0].format(appendix.get(key, ''))
|
59
|
+
|
60
|
+
def diff(self, key: str, search_list: list, symmetrical: bool=False) -> set:
|
61
|
+
pattern = KEYWORD[key][2]
|
62
|
+
separator = self.get_separator(key)
|
63
|
+
def field_set(source: list) -> set:
|
64
|
+
return set(
|
65
|
+
re.sub(pattern, '', fld.strip()).lower()
|
66
|
+
for string in source
|
67
|
+
for fld in re.split(separator, string)
|
68
|
+
)
|
69
|
+
s1 = field_set(search_list)
|
70
|
+
s2 = field_set(self.values.get(key, []))
|
71
|
+
if symmetrical:
|
72
|
+
return s1.symmetric_difference(s2)
|
73
|
+
return s1 - s2
|
74
|
+
|
40
75
|
def delete(self, search: str, keys: list=USUAL_KEYS):
|
41
76
|
for key in keys:
|
42
77
|
result = []
|
@@ -54,9 +89,10 @@ class Field:
|
|
54
89
|
|
55
90
|
@classmethod
|
56
91
|
def format(cls, name: str, main: SQLObject) -> str:
|
92
|
+
name = name.strip()
|
57
93
|
if name == '_':
|
58
94
|
name = '*'
|
59
|
-
|
95
|
+
elif '.' not in name:
|
60
96
|
name = f'{main.alias}.{name}'
|
61
97
|
if Function in cls.__bases__:
|
62
98
|
name = f'{cls.__name__}({name})'
|
@@ -319,20 +355,19 @@ class JoinType(Enum):
|
|
319
355
|
|
320
356
|
class Select(SQLObject):
|
321
357
|
join_type: JoinType = JoinType.INNER
|
322
|
-
REGEX =
|
358
|
+
REGEX = {}
|
323
359
|
|
324
360
|
def __init__(self, table_name: str='', **values):
|
325
361
|
super().__init__(table_name)
|
326
362
|
self.__call__(**values)
|
327
363
|
self.break_lines = True
|
328
364
|
|
365
|
+
def update_values(self, key: str, new_values: list):
|
366
|
+
for value in self.diff(key, new_values):
|
367
|
+
self.values.setdefault(key, []).append(value)
|
368
|
+
|
329
369
|
def add(self, name: str, main: SQLObject):
|
330
|
-
|
331
|
-
for value in new_values:
|
332
|
-
old_values = main.values.get(key, [])
|
333
|
-
if value not in old_values:
|
334
|
-
main.values[key] = old_values + [value]
|
335
|
-
update_values(
|
370
|
+
main.update_values(
|
336
371
|
FROM, [
|
337
372
|
'{jt}JOIN {tb} {a2} ON ({a1}.{f1} = {a2}.{f2})'.format(
|
338
373
|
jt=self.join_type.value,
|
@@ -343,14 +378,18 @@ class Select(SQLObject):
|
|
343
378
|
] + self.values[FROM][1:]
|
344
379
|
)
|
345
380
|
for key in USUAL_KEYS:
|
346
|
-
update_values(key, self.values.get(key, []))
|
381
|
+
main.update_values(key, self.values.get(key, []))
|
347
382
|
|
348
383
|
def __add__(self, other: SQLObject):
|
384
|
+
if self.table_name.lower() == other.table_name.lower():
|
385
|
+
for key in USUAL_KEYS:
|
386
|
+
self.update_values(key, other.values.get(key, []))
|
387
|
+
return self
|
349
388
|
foreign_field, primary_key = ForeignKey.find(self, other)
|
350
389
|
if not foreign_field:
|
351
390
|
foreign_field, primary_key = ForeignKey.find(other, self)
|
352
391
|
if foreign_field:
|
353
|
-
if primary_key
|
392
|
+
if primary_key:
|
354
393
|
PrimaryKey.add(primary_key, self)
|
355
394
|
self.add(foreign_field, other)
|
356
395
|
return other
|
@@ -380,10 +419,8 @@ class Select(SQLObject):
|
|
380
419
|
return self
|
381
420
|
|
382
421
|
def __eq__(self, other: SQLObject) -> bool:
|
383
|
-
def sorted_values(obj: SQLObject, key: str) -> list:
|
384
|
-
return sorted(obj.values.get(key, []))
|
385
422
|
for key in KEYWORD:
|
386
|
-
if
|
423
|
+
if self.diff(key, other.values.get(key, []), True):
|
387
424
|
return False
|
388
425
|
return True
|
389
426
|
|
@@ -396,15 +433,52 @@ class Select(SQLObject):
|
|
396
433
|
|
397
434
|
@classmethod
|
398
435
|
def parse(cls, txt: str) -> list[SQLObject]:
|
399
|
-
|
400
|
-
|
436
|
+
def find_last_word(pos: int) -> int:
|
437
|
+
SPACE, WORD = 1, 2
|
438
|
+
found = set()
|
439
|
+
for i in range(pos, 0, -1):
|
440
|
+
if txt[i] in [' ', '\t', '\n']:
|
441
|
+
if sum(found) == 3:
|
442
|
+
return i
|
443
|
+
found.add(SPACE)
|
444
|
+
if txt[i].isalpha():
|
445
|
+
found.add(WORD)
|
446
|
+
elif txt[i] == '.':
|
447
|
+
found.remove(WORD)
|
448
|
+
def find_parenthesis(pos: int) -> int:
|
449
|
+
for i in range(pos, len(txt)-1):
|
450
|
+
if txt[i] == ')':
|
451
|
+
return i+1
|
401
452
|
if not cls.REGEX:
|
402
|
-
keywords = '|'.join(KEYWORD)
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
tables = [t.strip() for t in re.split('JOIN|LEFT|RIGHT|ON', values[FROM]) if t.strip()]
|
453
|
+
keywords = '|'.join(k + r'\b' for k in KEYWORD)
|
454
|
+
flags = re.IGNORECASE + re.MULTILINE
|
455
|
+
cls.REGEX['keywords'] = re.compile(f'({keywords})', flags)
|
456
|
+
cls.REGEX['subquery'] = re.compile(r'(\w\.)*\w+ +in +\(SELECT.*?\)', flags)
|
407
457
|
result = {}
|
458
|
+
found = cls.REGEX['subquery'].search(txt)
|
459
|
+
while found:
|
460
|
+
start, end = found.span()
|
461
|
+
inner = txt[start: end]
|
462
|
+
if inner.count('(') > inner.count(')'):
|
463
|
+
end = find_parenthesis(end)
|
464
|
+
inner = txt[start: end-1]
|
465
|
+
fld, *inner = re.split(r' IN | in', inner, maxsplit=1)
|
466
|
+
if fld.upper() == 'NOT':
|
467
|
+
pos = find_last_word(start)
|
468
|
+
fld = txt[pos: start].strip() # [To-Do] Use the value of `fld`
|
469
|
+
start = pos
|
470
|
+
class_type = NotSelectIN
|
471
|
+
else:
|
472
|
+
class_type = SelectIN
|
473
|
+
obj = class_type.parse(
|
474
|
+
' '.join(re.sub(r'^\(', '', s.strip()) for s in inner)
|
475
|
+
)[0]
|
476
|
+
result[obj.alias] = obj
|
477
|
+
txt = txt[:start-1] + txt[end+1:]
|
478
|
+
found = cls.REGEX['subquery'].search(txt)
|
479
|
+
tokens = [t.strip() for t in cls.REGEX['keywords'].split(txt) if re.findall(r'\w+', t)]
|
480
|
+
values = {k.upper(): v for k, v in zip(tokens[::2], tokens[1::2])}
|
481
|
+
tables = [t.strip() for t in re.split('JOIN|LEFT|RIGHT|ON', values[FROM]) if t.strip()]
|
408
482
|
for item in tables:
|
409
483
|
if '=' in item:
|
410
484
|
a1, f1, a2, f2 = [r.strip() for r in re.split('[().=]', item) if r]
|
@@ -417,23 +491,52 @@ class Select(SQLObject):
|
|
417
491
|
for key in USUAL_KEYS:
|
418
492
|
if not key in values:
|
419
493
|
continue
|
420
|
-
separator =
|
421
|
-
' ' if key == WHERE else ''
|
422
|
-
)
|
423
|
-
fields = [
|
424
|
-
fld.strip() for fld in re.split(
|
425
|
-
separator, values[key]
|
426
|
-
) if len(tables) == 1
|
427
|
-
or re.findall(f'^[( ]*{obj.alias}.', fld)
|
428
|
-
]
|
494
|
+
separator = cls.get_separator(key)
|
429
495
|
obj.values[key] = [
|
430
|
-
|
496
|
+
Field.format(fld, obj)
|
497
|
+
for fld in re.split(separator, values[key])
|
498
|
+
if len(tables) == 1 or re.findall(f'\b*{obj.alias}[.]', fld)
|
431
499
|
]
|
432
500
|
result[obj.alias] = obj
|
433
501
|
return list( result.values() )
|
434
502
|
|
503
|
+
class SelectIN(Select):
|
504
|
+
condition_class = Where
|
435
505
|
|
436
|
-
class SubSelect(Select):
|
437
506
|
def add(self, name: str, main: SQLObject):
|
438
507
|
self.break_lines = False
|
439
|
-
|
508
|
+
self.condition_class.list(self).add(name, main)
|
509
|
+
|
510
|
+
SubSelect = SelectIN
|
511
|
+
|
512
|
+
class NotSelectIN(SelectIN):
|
513
|
+
condition_class = Not
|
514
|
+
|
515
|
+
|
516
|
+
if __name__ == "__main__":
|
517
|
+
query_list = Select.parse("""
|
518
|
+
SELECT
|
519
|
+
cas.role,
|
520
|
+
m.title,
|
521
|
+
m.release_date,
|
522
|
+
a.name as actors_name
|
523
|
+
FROM
|
524
|
+
Actor a
|
525
|
+
LEFT JOIN Cast cas ON (a.cast = cas.id)
|
526
|
+
LEFT JOIN Movie m ON (cas.movie = m.id)
|
527
|
+
WHERE
|
528
|
+
m.genre NOT in (SELECT g.id from Genres g where g.name in ('sci-fi', 'horror', 'distopia'))
|
529
|
+
AND (m.hashtag = '#cult' OR m.awards LIKE '%Oscar%')
|
530
|
+
AND m.id IN (select DISTINCT r.movie FROM Review r GROUP BY r.movie HAVING Avg(r.rate) > 4.5)
|
531
|
+
AND a.age <= 69 AND a.age >= 45
|
532
|
+
ORDER BY
|
533
|
+
m.release_date
|
534
|
+
""")
|
535
|
+
for query in query_list:
|
536
|
+
descr = ' {} ({}) '.format(
|
537
|
+
query.table_name,
|
538
|
+
query.__class__.__name__
|
539
|
+
)
|
540
|
+
print(descr.center(50, '-'))
|
541
|
+
print(query)
|
542
|
+
print('='*50)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sql_blocks
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.5
|
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
|
@@ -55,7 +55,7 @@ You can specify your own alias: `a = Select('Actor a')`
|
|
55
55
|
3.2 -- Sub-queries:
|
56
56
|
```
|
57
57
|
query = Select('Movie m', title=Field,
|
58
|
-
id=
|
58
|
+
id=SelectIN(
|
59
59
|
'Review r',
|
60
60
|
rate=Where.gt(4.5),
|
61
61
|
movie_id=Distinct
|
@@ -86,12 +86,13 @@ query = Select('Movie m', title=Field,
|
|
86
86
|
|
87
87
|
3.4 -- Negative conditions use the _Not_ class instead of _Where_
|
88
88
|
```
|
89
|
-
franchise
|
90
89
|
based_on_book=Not.is_null()
|
91
90
|
```
|
92
91
|
|
93
92
|
3.5 -- List of values
|
93
|
+
```
|
94
94
|
hash_tag=Where.list(['space', 'monster', 'gore'])
|
95
|
+
```
|
95
96
|
|
96
97
|
---
|
97
98
|
### 4 - A field can be two things at the same time:
|
@@ -100,7 +101,7 @@ hash_tag=Where.list(['space', 'monster', 'gore'])
|
|
100
101
|
- This means that the field will appear in the results and also that the query will be ordered by that field.
|
101
102
|
* Applying **GROUP BY** to item 3.2, it would look like this:
|
102
103
|
```
|
103
|
-
|
104
|
+
SelectIN(
|
104
105
|
'Review r', movie=[GroupBy, Distinct],
|
105
106
|
rate=Having.avg(Where.gt(4.5))
|
106
107
|
)
|
@@ -248,13 +249,13 @@ m = Select...
|
|
248
249
|
9.1
|
249
250
|
```
|
250
251
|
a1 = Select.parse('''
|
251
|
-
SELECT gender,
|
252
|
+
SELECT gender, Max(act.age) FROM Actor act
|
252
253
|
WHERE act.age <= 69 AND act.age >= 45
|
253
254
|
GROUP BY gender
|
254
255
|
''')[0]
|
255
256
|
|
256
257
|
a2 = Select('Actor',
|
257
|
-
age=Between(45, 69),
|
258
|
+
age=[ Between(45, 69), Max ],
|
258
259
|
gender=[GroupBy, Field]
|
259
260
|
)
|
260
261
|
```
|
@@ -282,7 +283,7 @@ m = Select...
|
|
282
283
|
|
283
284
|
9.3
|
284
285
|
```
|
285
|
-
best_movies =
|
286
|
+
best_movies = SelectIN(
|
286
287
|
Review=Table('role'),
|
287
288
|
rate=[GroupBy, Having.avg(Where.gt(4.5))]
|
288
289
|
)
|
@@ -302,13 +303,13 @@ m2 = Select(
|
|
302
303
|
---
|
303
304
|
|
304
305
|
### 10 - CASE...WHEN...THEN
|
305
|
-
Select(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
306
|
+
Select(
|
307
|
+
'Product',
|
308
|
+
label=Case('price').when(
|
309
|
+
lt(50), 'cheap'
|
310
|
+
).when(
|
311
|
+
gt(100), 'expensive'
|
312
|
+
).else_value(
|
313
|
+
'normal'
|
314
|
+
)
|
313
315
|
)
|
314
|
-
)
|
@@ -81,3 +81,19 @@ def many_texts_to_objects():
|
|
81
81
|
WHERE ( m.genre = 'Sci-Fi' OR m.awards LIKE '%Oscar%' ) GROUP BY director
|
82
82
|
""")[0]
|
83
83
|
return actor, cast, movie
|
84
|
+
|
85
|
+
def two_queries_same_table() -> Select:
|
86
|
+
txt1 = """SELECT p.name, p.category
|
87
|
+
,p.price,p.promotional FROM product p
|
88
|
+
where p.category in (6,14,29,35,78)
|
89
|
+
AND p.Status = p.last_st ORDER BY p.EAN"""
|
90
|
+
txt2 = """select stock_amount, EAN,Name ,expiration_date
|
91
|
+
from PRODUCT where price < 357.46 and status = Last_ST order by ean"""
|
92
|
+
return Select.parse(txt1)[0] + Select.parse(txt2)[0]
|
93
|
+
|
94
|
+
def select_product() -> Select:
|
95
|
+
return Select(
|
96
|
+
Product=Table('name,promotional,stock_amount,expiration_date'),
|
97
|
+
category=[Where.list([6,14,29,35,78]),Field], EAN=[Field, OrderBy],
|
98
|
+
price=[Where.lt(357.46),Field], status=Where('= Last_st')
|
99
|
+
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|