sqlnomad

Monadic SQL wrapper


License
MIT
Install
pip install sqlnomad==0.1.05

Documentation

sqlnomad

Monadic wrapper around SQL syntax

Installation

The package can be installed with pip using the following command:

$ pip install sqlnomad

Description

SQL is verbose, and the differences between SQL dialects make migration between databases tedious. This project aims to solve both issues by abstracting SQL syntax into a series of subquery primitives and providing monadic functions to transform them.

Examples

>>> from sqlnomad import *

>>> customers = StorageTable(
...     storage_name="customer",
...     display_name="Customers",
...     fields=[
...         StorageField("id", "Customer ID"),
...         StorageField("first_name", "First Name"),
...         StorageField("last_name", "Last Name")
...     ],
...     dialect=SqlDialect.MSSQL,
...     schema="dim"
... )

>>> sales = StorageTable(
...     storage_name="sales",
...     display_name="Sales",
...     fields=[
...         StorageField("id", "Sales ID"),
...         StorageField("customer_id", "Customer ID"),
...         StorageField("sales_date", "Sales Date"),
...         StorageField("sales_amount", "Sales Amount")
...     ],
...     dialect=SqlDialect.MSSQL,
...     schema="fact"
... )

We can combine storage tables using the join function

>>> transactions = Query(customers).bind(
...     join(
...         right_table=sales,
...         left_field_alias="Customer ID",
...         right_field_alias="Customer ID"
...     )
... )

Aggregations are performed with the aggregate function. Let's also sort the results by the total Sales Amount in descending order.

>>> total_sales = transactions.bind(
...     aggregate(
...         group_by_fields=["First Name", "Last Name"],
...         aggregations={"Sales Amount": AggregationMethod.SUM}
...     )
... ).bind(order_by({"Sales Amount": SortOrder.DESCENDING}))
>>> total_sales.sql

This generates the following query:

SELECT [Customers 3].[First Name] AS [First Name],
       [Customers 3].[Last Name] AS [Last Name],
       [Customers 3].[Sales Amount] AS [Sales Amount]
FROM (
  SELECT [Customers 2].[First Name] AS [First Name],
         [Customers 2].[Last Name] AS [Last Name],
         SUM([Customers 2].[Sales Amount]) AS [Sales Amount]
  FROM (
      SELECT [Customers].[Customer ID] AS [Customer ID],
             [Customers].[First Name] AS [First Name],
             [Customers].[Last Name] AS [Last Name],
             [Sales].[Sales Amount] AS [Sales Amount],
             [Sales].[Sales Date] AS [Sales Date],
             [Sales].[Sales ID] AS [Sales ID]
      FROM (
          SELECT [id] AS [Customer ID],
                 [first_name] AS [First Name],
                 [last_name] AS [Last Name]
          FROM [dim].[customer]
      ) [Customers]
      INNER JOIN (
          SELECT [id] AS [Sales ID],
                 [customer_id] AS [Customer ID],
                 [sales_date] AS [Sales Date],
                 [sales_amount] AS [Sales Amount]
          FROM [fact].[sales]
      ) [Sales]
      ON [Customers].[Customer ID] = [Sales].[Customer ID]
  ) [Customers 2]
  GROUP BY [Customers 2].[First Name],
           [Customers 2].[Last Name]
) [Customers 3]
ORDER BY [Customers 3].[Sales Amount] DESC

We can also combine storage tables with raw sql Subquery objects, for example:

>>> sql = """
...     SELECT c.id AS [Customer ID]
...     FROM dim.customer AS c
...     JOIN fact.sales AS s
...       ON c.id = s.customer_id
...     GROUP BY c.id
...     HAVING SUM(sales_amount) >= 10000
...  """
>>> whales = Subquery(
...    sql=sql,
...    fields=[SqlField(alias="Customer ID", definition="Customer ID")],
...    dialect=SqlDialect.MSSQL,
...    alias="Whales"
... )
>>> qry = Query(whales).bind(
...     join(
...         right_table=transactions,
...         left_field_alias="Customer ID",
...         right_field_alias="Customer ID"
...     )
... )

We can also filter our queries using the find function:

>>> and_or_filter = transactions.bind(
...     find([
...         [SqlFilter("First Name", FilterOperator.STARTS_WITH, "Steve"),
...          SqlFilter("Last Name", FilterOperator.EQUALS, "Smith")],
...         [SqlFilter("Sales Amount", FilterOperator.GREATER_THAN, 10)]
...     ])
... )

Note

This project is in its infancy. The API will be subject to drastic changes up until its 1.0 release.