Sqlx join table with same fields - sql

I'm using Go 1.17 with Sqlx but I don't understand how I can join my table correctly.
Here is my structs (my join isn't logic I'm just testing jointure with sqlx)
Table album:
package album
import ".../api/v1/movie"
type Album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
MovieId int `json:"movie_id" db:"movie_id"`
movie.Movie
}
Table movie:
package movie
type Movie struct {
ID string `json:"id"`
Year uint16 `json:"year"`
RentNumber uint32 `json:"rent_number" db:"rent_number"`
Title string `json:"title"`
Author string `json:"author"`
Editor string `json:"editor"`
Index string `json:"index"`
Bib string `json:"bib"`
Ref string `json:"ref"`
Cat1 string `json:"cat_1" db:"cat_1"`
Cat2 string `json:"cat_2" db:"cat_2"`
}
And this is how I do my join:
albums := []Album{}
r.db.Select(&albums, "SELECT * FROM album a INNER JOIN movie m ON (m.id=a.movie_id)")
The problem is that these 2 tables have the same id field so the album id is overridden by the movie id and I lost it.
How can I do to ignore the movie id field (because I got it in the movie_id field and keep the field id for the album id ?

You can give one of your id fields an id tag like:
type Album struct {
ID string `json:"id" id:"album_id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
movie.Movie
}
and then make a query which aliases the id field to album_id like:
SELECT movie.id as id, album.id as album_id, ... FROM album ...
Just keep in mind that you now need to use this column name in your named queries as well.

Related

In GoLang how do you scan in a sql result where some records may have a null join?

I'm not entirely sure the best way to phrase this problem, but hopefully my description and the code will show what I mean.
I'm building an API that uses a SQL db. One of the record types, Order can contain a Key, but the key ID is null until the Order is finalized. I have a function that queries the DB for all orders, joined with the Key DB so that the key details are populated if the ID is not null.
How do I scan in Orders where some have keyId null and some do not?
Order struct:
type orderDb struct {
id int
certificate certificateDb
location string
status string
knownRevoked bool
err sql.NullString // stored as json object
expires sql.NullInt32
dnsIdentifiers commaJoinedStrings // will be a comma separated list from storage
authorizations commaJoinedStrings // will be a comma separated list from storage
finalize string
finalizedKey *keyDb
certificateUrl sql.NullString
pem sql.NullString
validFrom sql.NullInt32
validTo sql.NullInt32
createdAt int
updatedAt int
}
keyDb struct
type keyDb struct {
id int
name string
description string
algorithmValue string
pem string
apiKey string
apiKeyViaUrl bool
createdAt int
updatedAt int
}
Partial SQL query & scan
query := `
SELECT
ao.id, ao.status, ao.known_revoked, ao.error, ao.dns_identifiers, ao.valid_from,
ao.valid_to, ao.created_at, ao.updated_at,
pk.id, pk.name,
c.id, c.name, c.subject,
aa.id, aa.name, aa.is_staging
FROM
acme_orders ao
LEFT JOIN private_keys pk on (ao.finalized_key_id = pk.id)
LEFT JOIN certificates c on (ao.certificate_id = c.id)
LEFT JOIN acme_accounts aa on (c.acme_account_id = aa.id)
err = rows.Scan(
&oneOrder.id,
&oneOrder.status,
&oneOrder.knownRevoked,
&oneOrder.err,
&oneOrder.dnsIdentifiers,
&oneOrder.validFrom,
&oneOrder.validTo,
&oneOrder.createdAt,
&oneOrder.updatedAt,
&oneOrder.finalizedKey.id,
&oneOrder.finalizedKey.name,
Essentially sometimes key id and name are null because the key is null. How do I set finalizedKey to null when this is the case, but scan in the values when the key isn't null?
I don't really want to do a separate query for keys because the slice of Orders could have 20, 50, or 100+ records and I don't want to do 101 queries to return a slice of 100 Orders.

GORM preload: How to use a custom table name

I have a GORM query with a preload that works just fine because I'm binding it to a struct called "companies" which is also the name of the corresponding database table:
var companies []Company
db.Preload("Subsidiaries").Joins("LEFT JOIN company_prod ON company_products.company_id = companies.id").Where("company_products.product_id = ?", ID).Find(&companies)
Now I want to do something similar, but bind the result to a struct that does not have a name that refers to the "companies" table:
var companiesFull []CompanyFull
db.Preload("Subsidiaries").Joins("LEFT JOIN company_prod ON company_products.company_id = companies.id").Where("company_products.product_id = ?", ID).Find(&companies)
I've simplified the second call for better understanding, the real call has more JOINs and returns more data, so it can't be bound to the "companies" struct.
I'm getting an error though:
column company_subsidiaries.company_full_id does not exist
The corresponding SQL query:
SELECT * FROM "company_subsidiaries" WHERE "company_subsidiaries"."company_full_id" IN (2,1)
There is no "company_subsidiaries.company_full_id", the correct query should be:
SELECT * FROM "company_subsidiaries" WHERE "company_subsidiaries"."company_id" IN (2,1)
The condition obviously gets generated from the name of the struct the result is being bound to. Is there any way to specify a custom name for this case?
I'm aware of the Tabler interface technique, however it doesn't work for Preload I believe (tried it, it changes the table name of the main query, but not the preload).
Updated: More info about the DB schema and structs
DB schema
TABLE companies
ID Primary key
OTHER FIELDS
TABLE products
ID Primary key
OTHER FIELDS
TABLE subsidiaries
ID Primary key
OTHER FIELDS
TABLE company_products
ID Primary key
Company_id Foreign key (companies.id)
Product_id Foreign key (products.id)
TABLE company_subsidiaries
ID Primary key
Company_id Foreign key (companies.id)
Subsidiary_id Foreign key (subsidiaries.id)
Structs
type Company struct {
Products []*Product `json:"products" gorm:"many2many:company_products;"`
ID int `json:"ID,omitempty"`
}
type CompanyFull struct {
Products []*Product `json:"products" gorm:"many2many:company_products;"`
Subsidiaries []*Subsidiary `json:"subsidiaries" gorm:"many2many:company_products;"`
ID int `json:"ID,omitempty"`
}
type Product struct {
Name string `json:"name"`
ID int `json:"ID,omitempty"`
}
type Subsidiary struct {
Name string `json:"name"`
ID int `json:"ID,omitempty"`
}
Generated SQL (by GORM)
SELECT * FROM "company_subsidiaries" WHERE "company_subsidiaries"."company_full_id" IN (2,1)
SELECT * FROM "subsidiaries" WHERE "subsidiaries"."id" IN (NULL)
SELECT companies.*, company_products.*, FROM "companies" LEFT JOIN company_products ON company_products.company_id = companies.id WHERE company_products.product_id = 1
Seems like the way to go in this case may be to customize the relationship in your CompanyFull model. Using joinForeignKey the following code works.
type CompanyFull struct {
Products []*Product `json:"products" gorm:"many2many:company_products;joinForeignKey:ID"`
Subsidiaries []*Subsidiary `json:"subsidiaries" gorm:"many2many:company_subsidiaries;joinForeignKey:ID"`
ID int `json:"ID,omitempty"`
}
func (CompanyFull) TableName() string {
return "companies"
}
func main(){
...
result := db.Preload("Subsidiaries").Joins("LEFT JOIN company_products ON company_products.company_id = companies.id").Where("company_products.product_id = ?", ID).Find(&companies)
if result.Error != nil {
log.Println(result.Error)
} else {
log.Printf("%#v", companies)
}
For more info regarding customizing the foreign keys used in relationships, take a look at the docs https://gorm.io/docs/many_to_many.html#Override-Foreign-Key

How to filter table with entity from other tables related by many to many relationship using GORM?

I have Product table which is connected with the other two tables Categorie & AttributeValue using many to many relationships. I am using GORM as an ORM. go struct for those tables are like bellow.
type Product struct {
ProductID int `gorm:"column:product_id;primary_key" json:"product_id"`
Name string `gorm:"column:name" json:"name"`
Categories []Category `gorm:"many2many:product_category;foreignkey:product_id;association_foreignkey:category_id;association_jointable_foreignkey:category_id;jointable_foreignkey:product_id;"`
AttributeValues []AttributeValue `gorm:"many2many:product_attribute;foreignkey:product_id;association_foreignkey:attribute_value_id;association_jointable_foreignkey:attribute_value_id;jointable_foreignkey:product_id;"`
}
type Category struct {
CategoryID int `gorm:"column:category_id;primary_key" json:"category_id"`
Name string `gorm:"column:name" json:"name"`
Products []Product `gorm:"many2many:product_category;foreignkey:category_id;association_foreignkey:product_id;association_jointable_foreignkey:product_id;jointable_foreignkey:category_id;"`
}
type AttributeValue struct {
AttributeValueID int `gorm:"column:attribute_value_id;primary_key" json:"attribute_value_id"`
AttributeID int `gorm:"column:attribute_id" json:"attribute_id"`
Value string `gorm:"column:value" json:"value"`
Products []Product `gorm:"many2many:product_attribute;foreignkey:attribute_value_id;association_foreignkey:product_id;association_jointable_foreignkey:product_id;jointable_foreignkey:attribute_value_id;"`
}
If I want to query Product table by category I can do it like bellow which will return all products in a category with category_id 3.
cat := model.Category{}
s.db.First(&cat, "category_id = ?", 3)
products := []*model.Product{}
s.db.Model(&cat).Related(&products, "Products")
If I want to query the Product table by both Category & AttributeValue how can I do that? Suppose I want to find all the products that are in category with category_id 3 and has AttributeValue with attribute_value_id 2?
I found some ways to query products based on category and AttributeValue. The best way I got is like bellow
products := []*model.Product{}
s.db.Joins("INNER JOIN product_attribute ON product_attribute.product_id = " +
"product.product_id AND product_attribute.attribute_value_id in (?)", 2).
Joins("INNER JOIN product_category ON product_category.product_id = " +
"product.product_id AND product_category.category_id in (?)", 3).
Find(&products)
After executing this, products slice will be populated with all the products that are in the category with category_id 3 and has AttributeValue with attribute_value_id 2. We can pass slice of string if we need to find products in multiple Category & AttributeValue.

Oracle - Nested Table fetching results

I have a problem with nested tables. I don't know if I can fetch the result in the way I want them.
For example I have:
create type Name as Object(
firstname varchar2(20),
lastname varchar2(20))final;
create type Author as Object(
authorName Name);
create type Author_list as table of Author;
create table books(bookID int primary key, author Author_list) nested table author store as Author_nested;
When I fetch the result with:
select b.bookID, a.authorname.firstname||' '||a.authorname.lastname
from books b, table(b.author) a;
I am getting for each author a specific row. I want that for a specific bookID the authors to display in that row and separated with commas. Is that possible?
ex: bookID, authorname
1 , ab, cd, de
Yes, it is possible (one way is to use LISTAGG):
select b.bookID,
LISTAGG(a.authorname.firstname||' '||a.authorname.lastname, ',')
WITHIN GROUP(ORDER BY b.BookId) AS authorname
from books b, table(b.author) a
GROUP BY b.bookID

SQL: How to separate string values separated by commas?

I'm trying to create a relational database of all the movies I have watched.
I used IMDb to rate the movies I've seen and used the site's export capability to get the data in a .csv file which I uploaded to Microsoft Access. However, the "Genre" column is a many-to-many relationship that I am hoping to turn into a one-to-many relationship.
I would like to have a table called GENRE_ID that assigns each genre a numerical ID. Then I'd have another table where each instance would have the movie ID ("const"), line item number, and GENRE_ID.
So it might look like:
const line_item MOVIE_ID
tt0068646 1 1 (if MOVIE_ID: 1 = "crime")
tt0068646 2 2 (if MOVIE_ID: 2 = "drama")
Here's a link to the image of my database's current state. Thank you so much for your help. This is a project I'm doing to learn more on my own time.
Basically, when you have a one-to-many relationship, you should use a table for that relationship
In your case, I would recommend to have 3 table:
Film table : contains information like your current table ,except Genres
Genre table : contains (at least) Id and Name
Film_Genre table : contains Film_Id, GenreId.
For example
In your genre table, your data would be
row 1: Id =1 , Name = "Crime"
row 2: Id = 2, Name = drama,
and so on
your Film_Genre table would be something like:
row1: Film_Id = tt0068646, GenreId = 1,
row2: Film_Id = tt0068646, GenreId = 2
row3: Film_Id = tt0082971, GenreId = 2
and so on
(I supposed that you use "const" column as Id of Film table, if not, you should have your own Id)
Of course, it take you a litte bit effort to transform your current database to this database.
Some notes on a way to a solution.
A table of genres
ID Genre
1 Action
2 Adventure
3 Thriller
4 War
An import table
Const GenreList
tt00 Action, Adventure, Thriller, War
A query
SELECT ti.Const, ti.GenreList, tg.Genre
FROM Imports as ti, Genres as tg
WHERE ti.GenreList Like "*" & tg.Genre & "*"