Open Irupt opened 8 months ago
Yes. See this recipe https://kysely.dev/docs/recipes/schemas
Thanks, but unfortunately it only works with a schema but not with a database.schema
Oh, that's true. I'm not sure if we can make that work without complicating everything too much. But let's take a look. For now it's impossible to do this without using raw SQL.
// PersonTable is the table interface.
.selectFrom(sql<PersonTable>`foo.bar.baz.person`.as('person'))
or something like:
function table<T>(...t: string[]) {
return sql<T>`${sql.id(...t)}`
}
...
.selectFrom(table<PersonTable>("foo", "bar", "baz", "spam").as('person'))
Just want to add this here:
With some slight modifications, the solution above covers all of my needs for cross database joins.
I have a bunch of databases that I do not have much control over and it's structured such that there are a few situations in which I need to join two tables across databases. I also have kysely-codegen writing all of my database schemas out to separate TypeScript files. I have some custom codegen alongside that which will create a TypeScript type that includes all of the databases from my server like so in file called schemas/index.ts:
import { DB as DB1 } from "./db1";
import { DB as DB2 } from "./db2";
export type Server = {
DB1: DB1;
DB2: DB2;
};
where ./db1 is something like
export interface DB {
person: {
id: Generated<string>
first_name: string
last_name: string | null
created_at: Generated<Date>
age: number
}
}
and ./db2 is something like
export interface DB {
pet: {
id: Generated<string>
name: string
owner_id: string
species: 'cat' | 'dog'
is_favorite: boolean
}
}
DB1 and DB2 would be the name of my databases.
All of my databases share the same schema name, so I can just do something like the following:
import { Server } from "./schemas";
export function table<
DB extends keyof Server,
Table extends string & keyof Server[DB]
>(database: DB, table: Table) {
return sql<Server[DB][Table]>`${sql.id(database, "dbo", table)}`;
}
With that, you can use it as such for a join:
const result = await db1.selectFrom("person")
.innerJoin(table("DB2", "pet").as("pet"), "person.id", "pet.owner_id")
.selectAll()
.execute();
You can also use the json helpers as such:
const result = await db1.selectFrom("person")
.selectAll("person")
.select((eb) =>
jsonArrayFrom(
eb
.selectFrom(table("DB2", "pet").as("pet"))
.select(['pet.id', 'pet.name'])
.whereRef("pet.owner_id", "=", "person.id")
).as("pets")
)
.execute();
Both of these keep the type safety without requiring explicit generics and allowing you to join across databases.
Apologies if any of this syntax is slightly off, I more or less took the example from kysely's relations recipe, modified it with the tables helper function and to fit the schemas in my databases. Then I subbed the example table's names and schemas back in, but I think this covers the gist of it.
Edit: here's a kysely playground link which might be a bit more digestible.
Using sql
with as
works fine for most case.
But it raises error in UPDATE
query as
UPDATE db2.dbo.table AS t ...
which is not compatible with SQL server syntax
FROM
, it seems fine to me..as
, the query works but type error is raised as .updateTable
only accepts AliasedExpression
type
sql`db2.dbo.table` as unknown as AliasedExpression<T, 'table'>
EDIT: In case anyone want inter DB query and not using inter schema query (or you can filter them), you can use plugin like
class InterDBTransformer extends OperationNodeTransformer {
protected override transformTable(node: TableNode): TableNode {
const tableNode = super.transformTable(node);
if (tableNode.table.schema) {
return RawNode.create(
[`${tableNode.table.schema.name}.dbo.${tableNode.table.identifier.name}`],
[],
) as unknown as TableNode;
}
return tableNode;
}
}
class InterDBPlugin implements KyselyPlugin {
#transformer = new InterDBTransformer();
transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
return this.#transformer.transformNode(args.node);
}
async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
return args.result;
}
}
This will convert DB2.pet
to become DB2.dbo.pet
on query compile level without extra function calls.
Kysely Playground: link
Good morning, I work with Sql server on several databases. When I do a select it is common for me to join tables between several databases. Is there a way on Kysely to specify the database/schema/tablename?
Ex: Select * from dbname1.dbo.tablename1 T1 inner join dbname2.dbo.tablename2 T2 on T1.ID = T2.ID
tyvm