I have a table in SQLite3 with the following schema:
create table threads(
id integer primary key autoincrement,
submitter text,
body text,
title text,
subtime int -- Unix time
);
I'm inserting rows like this:
insert into threads (title, body, subtime, submitter) values
("I like ducks", "Don't you?", 1467664977640, "tom");
I'd like to to get the id column after I insert the thread. How can I do this? Ideally I could insert and retrieve the column in the same statement.
The Result interface allows you to access this value without having to execute another query:
res, err := db.Exec("INSERT ..."), someParam)
if err != nil {
println("Exec err:", err.Error())
} else {
id, err := res.LastInsertId()
if err != nil {
println("Error:", err.Error())
} else {
println("LastInsertId:", id)
}
}
I don't know if you can do that in one statement but immediately after the insert statement you can use the following statement to get the last auto incremented id:
SELECT last_insert_rowid()
Another way is to use the following statement:
select seq from sqlite_sequence where name="threads"
Related
I am using golang sqlx package and postgresql as the database. I have two simple tables
create table items (
id varchar unique primary key not null,
is_active bool not null default false
);
create table images (
item_id varchar not null,
link varchar unique not null,
index int not null,
foreign key (item_id) references items(id) on delete cascade
);
What I need to perform is to get all of the items that have assigned images to them in one query and do not manipulate with go variables afterwards
I have such code in order to perform that
type Image struct {
ItemID string `db:"item_id" json:"item_id"`
Index int `db:"index" json:"index"`
Link string `db:"link" json:"link"`
}
type Images []Image
func (t Images) Value() (driver.Value, error) {
b, err := json.Marshal(t); if err != nil {
return driver.Value(""), err
}
return driver.Value(string(b)), nil
}
func (t *Images) Scan (src interface{}) error {
var source []byte
if reflect.TypeOf(src) == nil {
return nil
}
switch src.(type) {
case string:
source = []byte(src.(string))
case []byte:
source = src.([]byte)
default:
return errors.New("incompatible type for images")
}
return json.Unmarshal(source, t)
}
func GetItems (active bool) (items []Item, err error) {
conn := postgres.Connection()
const query = `SELECT *, (
SELECT json_agg(images) AS images FROM (
SELECT images.link, images.index FROM images GROUP BY images.link, images.index HAVING bool_or(item_id=items.id) AND count(item_id) > 0
) as images
) FROM items`
if err := conn.Select(&items, query, active); err != nil {
logrus.WithError(err).Errorf("Can not get all items")
}
return
}
This works but I can have results with items that has no assigned images for them and this code seems tooo complicated for such simple at first sight task. Wish anyone can help me with that
If I understand correctly you wanna do this:
select i.id
from images img
join items i
on img.item_id = i.id
where i.isactive =1
group by i.id
With the help of #eshirvana I came to this solution:
SELECT items.*, json_agg(images) as images
FROM items
JOIN images
ON images.item_id = items.id
WHERE is_active=$1
GROUP BY items.id
I'm new using go and gorm. I'm trying to insert many values in one SQL query.
I wrote this query to add multiple conversations to a user:
relationUserConversation := make([][]uint, len(users))
for i, v := range users {
relationUserConversation[i] = []uint{conversation.ID, v}
}
result = r.db.Debug().Exec(
"INSERT INTO `user_has_conversations` (`user_has_conversations`.`conversation_id`, `user_has_conversations`.`user_id`) VALUES ?",
relationUserConversation, // If i do this it works relationUserConversation[0], relationUserConversation[1]
// The issue is because the query has this value "VALUES ((35,1),(35,2))", but should be to work (35,1),(35,2)
)
I also tried to add it directly with the conversation that would be what I would like to do, but I'm having issue trying to add the relation with the many to many because instead of creating the relation between the user and the conversation it tries to add the user.
My conversation model:
type Conversation struct {
ID uint `gorm:"primarykey"`
Users []*User `gorm:"many2many:user_has_conversations;"`
Messages []ConversationMessage
}
Would be great if i could create a new conversation with the related users in one query instead of creating first the conversation and after the relation to the users.
Below is a minimum working example using the Gorm Appends method (see documentation here) to create a many to many association between two (or more) models. Hopefully you can adapt this to your use case.
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Conversations []Conversation `gorm:"many2many:user_conversations;"`
}
type Conversation struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_conversations;"`
}
func main() {
db, err := gorm.Open(sqlite.Open("many2many.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
err = db.AutoMigrate(&User{}, &Conversation{})
if err != nil {
fmt.Print(err)
}
userOne := User{
Name: "User One",
}
userTwo := User{
Name: "User Two",
}
// Create users
db.Create(&userOne)
db.Create(&userTwo)
conversation := Conversation{
Name: "Conversation One",
}
// Create conversation
db.Create(&conversation)
// Append users
err = db.Model(&conversation).Association("Users").Append([]User{userOne, userTwo})
if err != nil {
fmt.Print(err)
}
for _, convUser := range conversation.Users {
fmt.Println("Hello I am in the conversation: " + convUser.Name)
}
// Clean up database
db.Delete(&userOne)
db.Delete(&userTwo)
db.Delete(&conversation)
}
Number of queries
If you enable Debug() on Gorm:
err = db.Debug().Model(&conversation).Association("Users").Append([]User{userOne, userTwo})
It shows this:
[0.144ms] [rows:2] INSERT INTO `user_conversations`
(`conversation_id`,`user_id`) VALUES (8,15),(8,16) ON CONFLICT DO NOTHING
The Values part is correct (what you were trying to do manually) and achieved using the ORM.
I'm struggling to properly utilize sqlx and the pq driver for Postgres to create a row in the database. Let's start simple:
I have a user, role and user_role table. I want to insert a role into the database and get the ID of the inserted row. This works flawlessly using the following sql:
const createRoleSQL = "INSERT INTO role (name) VALUES (:name) RETURNING id"
To make that work in go, I prepare the statement at some point:
createStmt, err := db.PrepareNamed(createRoleSQL)
if err != nil {
// ...
}
When creating, I run the query as part of a transaction tx. role is obviously a struct with the correct fields and db tags:
if err := tx.NamedStmt(createStmt).QueryRow(role).Scan(&role.ID); err != nil {
// ...
}
This works perfectly fine.
Now I wanted to extend that and insert a new role and assign it to a user:
const createUserRoleSQL = `
DO $$
DECLARE role_id role.id%TYPE;
BEGIN
INSERT INTO role (name) VALUES ($2) RETURNING id INTO role_id;
INSERT INTO user_role (user_id, role_id) VALUES ($1, role_id);
END $$`
createStmt, err := db.Preparex(createUserRoleSQL)
if err != nil {
// ...
}
if err := tx.Stmtx(createStmt).QueryRow(userID, role.Name).Scan(&role.ID); err != nil {
// ...
}
Unfortunately this fails with sql: expected 0 arguments, got 2. Is it possible to achieve what I want to do, with a single query?
I use PostgresQL and Go. I have a table which is called users. I try to write a function which will be able to update several columns. This function takes column names and user object (type struct). Can you help me with this?
This is a User struct in Go:
type User struct {
ID int json:"id"
Username string json:"username"
Password string json:"password"
FirstName string json:"first_name"
LastName string json:"last_name"
Email string json:"email"
}
This is SQL script which creates users table:
create table "users"
(
id serial not null
constraint user_pk
primary key,
username varchar(64) not null,
password varchar(128) not null,
first_name varchar(64) not null,
last_name varchar(64) not null,
email varchar(64) not null
);
1st example: I can pass first_name & last_name + full User object (postgres should update only these 2 fields)
2nd example: I can pass first_name & email & username + full User object (postgres should update only these 3 fields)
I have tried to do it with map but I could not:
func UpdateUser(db *sql.DB, m map[string]interface{}) (*User, error) {
for key, value := range m {
}
err := db.QueryRow(UPDATE "users" SET ())
}
Use UPDATE
In the example i assume you have written the function, which takes variables. Lets assume the variables are:
firstName
lastName
The SQL code is as follows:
UPDATE users
SET first_name = firstName, last_name= lastName
WHERE [condition]
You may find that you need to write a class for this with an overloaded constructor so that it takes any number of variables that you want.
Or better still write a function for each of the rows:
UPDATE users
SET first_name = firstName
WHERE [condition]
UPDATE users
SET last_name= lastName
WHERE [condition]
etc. It just means that the user will have to enter the items one at a time.
https://www.w3schools.com/sql/sql_update.asp
Here's my user update func:
I pass a User struct to the function and don't worry about which fields need to be updated, I just update them all. The caller is working with a User they got from a previous func that returned it from the DB (I'll show you the API handler if you want to see that).
After the update I call the Get function to get the freshly updated record from the DB and return the User struct. This is for certainty that the caller can see precisely what they just did.
// Update a User identified by id
func (u *UserModel) Update(user *models.User) (*models.User, error) {
stmt := `UPDATE user SET
first_name = ?,
last_name = ?,
email = ?,
phone = ?,
status_id = ?
WHERE id = ?`
var userStatus models.UserStatus
userStatusID := userStatus.GetID(user.Status)
_, err := u.DB.Exec(stmt, user.FirstName, user.LastName, user.Email, user.Phone, userStatusID, user.ID)
if err != nil {
if mysqlErr, ok := err.(*mysql.MySQLError); ok {
if mysqlErr.Number == 1062 && strings.Contains(mysqlErr.Message, "uk_user_email") {
return nil, models.ErrDuplicateEmail
}
}
return nil, err
}
user, err = u.Get(int(user.ID))
if err != nil {
return nil, err
}
return user, nil
}
I gathered that SQL Server does not return last inserted id automatically and I need to do it manually with: OUTPUT INSERTED.ID within SQL insert statement.
How do I pick it up later in Go code?
The function in question is:
func (sta *state) mkLogEntry(from time.Time, to time.Time, man bool) (id int64) {
qry := "INSERT INTO ROMEExportLog(FromDate,ToDate,ExecutedAt,ExecutedManually,ExportWasSuccessful,UpdatedDaysIrregular) OUTPUT INSERTED.ID " +
"VALUES(#FromDate,#ToDate,#ExecutedAt,#ExecutedManually,#ExportWasSuccessful,#UpdatedDaysIrregular)"
res, err := sta.db.Exec(qry,
sql.Named("FromDate", from),
sql.Named("ToDate", to),
sql.Named("ExecutedAt", time.Now()),
sql.Named("ExecutedManually", man),
sql.Named("ExportWasSuccessful", false),
sql.Named("UpdatedDaysIrregular", false),
)
if err != nil {
log.Fatal(err)
}
id, err = res.LastInsertId()
if err != nil {
log.Fatal(err)
}
return
}
The res.LastInsertId() returns There is no generated identity value.
FOR SQL SERVER:
This may be gimmicky... But I found that using QueryRow
will run multiple queries/commands and just return the LAST row.
if err := db.QueryRow(`
INSERT INTO [TABLE] ([COLUMN]) VALUES (?);
SELECT SCOPE_IDENTITY()`,
colValue
).Scan(&id); err != nil {
panic(err)
}
As long as SELECT SCOPE_IDENTITY() is the last row returned then this essentially does what I would have expected result.LastInsertId() to do.
The reason for this is because PostgreSQL does not return you the last inserted id. This is because last inserted id is available only if you create a new row in a table that uses a sequence.
If you actually insert a row in the table where a sequence is assigned, you have to use RETURNING clause. Something like this: INSERT INTO table (name) VALUES("val") RETURNING id.
I am not sure about your driver, but in pq you will do this in the following way:
lastInsertId := 0
err = db.QueryRow("INSERT INTO brands (name) VALUES($1) RETURNING id", name).Scan(&lastInsertId)