Автоматическое создание SQL-инструкций из неровного словаря Python

#python

#python

Вопрос:

Я пытаюсь выполнить цикл через dict для автоматического создания операторов SQL WHERE. Ниже приведен пример dict, а также желаемый результат (который был оптимизирован с использованием логических операторов OR / AND).

Существует 5 полей, требующих фильтрации (т.Е. field1, field2, field3, field4, value5). Dict сохраняет значения для фильтрации каждого поля в кортежах (где кортеж [0] и кортеж [1] представлены как нижняя и верхняя границы диапазона фильтрации).

В желаемом выводе (см. Ниже) вы заметите, что логические операторы были оптимизированы для устранения любой избыточности. Например, для GROUP1 значения field1 и field2 (т. Е. От 40000 до 50000 и от 0000 до ZZZZ соответственно) упоминаются только один раз в операторе WHERE, за ними следует оператор AND и ряд фильтров, в которых field3 (и соответствующие ему фильтры field4 / field5) разделены символом ORоператор (т.Е. Он находится в field3, где требуется фильтрация «вилки» и логический оператор OR). Для GROUP2 существует аналогичная конструкция; однако, field2 — это место, где возникает эта «вилка».

 input_range_dict = {
    "GROUP1": {
        (40000, 50000): { # field1
            ("0000", "ZZZZ"): { # field2
                ("000", "ZZZ"): { # field3
                    ("10000000", "99999999"): # field4
                         [(123, 123), (456, 456)], # field5 
                    ("20000000", "40000000"): # field4
                         [("000", "ZZZ")]}, # field5
                ("AAA", "BBB"): { # field3
                    ("10000000", "99999999"): # field4
                         [("000", "ZZZ")]} # field5
                               }
                         }
                },
    "GROUP2": {
        (70000, 90000): {
            ("0000", "ZZZZ"): { 
                ("000", "ZZZ"): {
                    ("10000000", "99999999"):
                         [(123, 123), (456, 456)],
                    ("20000000", "40000000"):
                         [("000", "ZZZ")]},
            ("AAAA", "ZZZZ"): {
                ("AAA", "BBB"): {
                    ("10000000", "99999999"):
                         [("000", "ZZZ")]}
                               }
                         }
                }
        }
}
 

Выходная строка для GROUP1:

 WHERE
  field1 BETWEEN "40000" AND "50000"
  AND field2 BETWEEN "0000" AND "ZZZZ"
  AND
    (
      (
        field3 BETWEEN "000" AND "ZZZ"
        AND
          (
            (
              field4 BETWEEN "10000000" AND "99999999"
              AND
                (
                  field5 BETWEEN "123" AND "123"
                  OR field5 BETWEEN "456" AND "456"
                 )
             )
             OR
             (
               field4 BETWEEN "20000000" AND "40000000"
               AND field5 BETWEEN "000" AND "ZZZ"
              )
           )
       )
       OR
       (
         field3 BETWEEN "AAA" AND BBB"
         AND field4 BETWEEN "10000000" AND "99999999"
         AND field5 BETWEEN "000" AND "ZZZ"
       )        
    )  
 

Выходная строка для GROUP2:

 WHERE
  field1 BETWEEN "70000" AND "90000" 
  AND 
    (
      field2 BETWEEN "0000" AND "ZZZZ"
      AND field3 BETWEEN "000" AND "ZZZ"
      AND
        ( 
          (
            field4 BETWEEN "10000000" AND "99999999"
            AND
              (
                 field5 BETWEEN "123" AND "123"
                 OR field5 BETWEEN "456" AND "456"
              )
           )
           OR
           (
             field4 BETWEEN "20000000" AND "40000000"
             AND field5 BETWEEN "000" AND "ZZZ"
           )
         )
      )
   OR
    (
      field2 BETWEEN "AAAA" AND "ZZZZ"
      AND field3 BETWEEN "AAA" AND "BBB"
      AND field4 BETWEEN "10000000" AND "99999999"
      AND field5 BETWEEN "000" AND "ZZZ"
     )
    
 

Ответ №1:

Вот решение, которое формирует условную логику на основе структуры группы, но без красивой печати:

 def gen_sql(d, c = 1, l = 0):
   if isinstance(d, list):
      return ("" if len(d) < 2 else "(")  ' or '.join(f'field{c} between {a} and {b}' for a, b in d)  ("" if len(d) < 2 else ")")
   return ' or '.join(("" if len(d) < 2 else "(") f'field{c} between {a} and {b} and {gen_sql(k, c = c 1, l = l (len(d) > 1))}' ("" if len(d) < 2 else ")") for (a, b), k in d.items())
   
input_range_dict = {'GROUP1': {(40000, 50000): {('0000', 'ZZZZ'): {('000', 'ZZZ'): {('10000000', '99999999'): [(123, 123), (456, 456)], ('20000000', '40000000'): [('000', 'ZZZ')]}, ('AAA', 'BBB'): {('10000000', '99999999'): [('000', 'ZZZ')]}}}}, 'GROUP2': {(70000, 90000): {('0000', 'ZZZZ'): {('000', 'ZZZ'): {('10000000', '99999999'): [(123, 123), (456, 456)], ('20000000', '40000000'): [('000', 'ZZZ')]}}, ('AAAA', 'ZZZZ'): {('AAA', 'BBB'): {('10000000', '99999999'): [('000', 'ZZZ')]}}}}}
for a, b in input_range_dict.items():
    print(f'{a}:')
    print(gen_sql(b))
    print()
 

Вывод:

 GROUP1:
field1 between 40000 and 50000 and field2 between 0000 and ZZZZ and (field3 between 000 and ZZZ and (field4 between 10000000 and 99999999 and (field5 between 123 and 123 or field5 between 456 and 456)) or (field4 between 20000000 and 40000000 and field5 between 000 and ZZZ)) or (field3 between AAA and BBB and field4 between 10000000 and 99999999 and field5 between 000 and ZZZ)

GROUP2:
field1 between 70000 and 90000 and (field2 between 0000 and ZZZZ and field3 between 000 and ZZZ and (field4 between 10000000 and 99999999 and (field5 between 123 and 123 or field5 between 456 and 456)) or (field4 between 20000000 and 40000000 and field5 between 000 and ZZZ)) or (field2 between AAAA and ZZZZ and field3 between AAA and BBB and field4 between 10000000 and 99999999 and field5 between 000 and ZZZ)
 

Примечание: значение словаря for GROUP2 не совсем отформатировано таким образом, чтобы оно соответствовало вашему желаемому результату: an }, должен идти перед ключом ("AAAA", "ZZZZ") .