# Concepts

## What is LETSQL?

LETSQL is a query engine with a pythonic dataframe interface, built on top of DataFusion that can be used to write multi-engine workflows.

## What is not LETSQL?
Is not a dataframe library, while it provides a familiar pythonic dataframe interface, LETSQL is equipped with a query optimizer and can
provide in-situ and federated query processing.

## Why LETSQL?

By using LETSQL, you will:

- Reduce errors thanks to a better Pythonic UX.
- Accelerate the development process by lowering the cognitive burden induced by using multiple interacting data systems.
- Gain in security by providing in-situ processing (the data does not move).
- Improve performance by avoiding data transfer and redundant operations.
- Reduce costs by easily swapping to the cheapest tool available.

## What is Multi-Engine?

What makes LETSQL stand-out against other Ibis backends, is that it can be use to build multi-engine workflows, by
multi-engine it means that it can an Ibis expression involving multiple backends in an optimal manner, segmenting the
expression and executing each part in-situ on the corresponding backend.


For the following example we are going to use an Ibis table from a Postgres connection and perform a join with an in-memory
pandas DataFrame.

```{python}
#| code-summary: multi-engine example
import pathlib

import ibis
import pandas as pd

import letsql as ls

ibis.options.interactive = True

# create the letsql connection
con = ls.connect()


# create the Ibis Postgres connection
pg = ibis.postgres.connect(
    host="localhost",
    port=5432,
    user="postgres",
    password="postgres",
    database="ibis_testing",
)


# register Postgres table
batting = con.register(pg.table("batting"), table_name="batting")

# register csv file
df = pd.read_csv("https://raw.githubusercontent.com/ibis-project/testing-data/master/csv/awards_players.csv")
awards_players = con.register(df, table_name="awards_players")

left = batting[batting.yearID == 2015]
right = awards_players[awards_players.lgID == "NL"].drop("yearID", "lgID").execute()

left.join(right, ["playerID"], how="semi")
```







