postgres - how to do batch update with array in where clause - postgresql

So,I was hoping to do this:
let statement = "update players set walk_count = unnest($1), x = unnest($2), y = unnest($3) where player_id = unnest($4)";
But the error I get is "message: "set-returning functions are not allowed in WHERE",
The only other way I can solve this is by doing individual updates, but I see the loop is taking a lot of time.

Assuming that each parameter ($1, $2, ...) is an array containing one item for each row you want to update, you should use a single unnest() call for all 4 arrays:
update players
set walk_count = v.wc,
x = v.x,
y = v.y
from (
select *
from unnest($1, $2, $3, $4)
) as v (wc, x, y, id)
where v.id = players.player_id

Related

Update a column using the value of another column per row

I'm trying to update a value in one table based on the value of another table in a one-to-many relationship, where there will be one movie but many reviews
Here's the schema for my tables
movie
+----+-------------+------+
| id | reviewCount |score |
+----+-------------+------+
review
+--------+---------+-------+
| userId | movieId | score |
+--------+---------+-------+
And here's the query I'm trying to run
UPDATE movie
SET score =
CASE
WHEN m."reviewCount" = 1
THEN 0
ELSE ((m.score * m."reviewCount") - r.score) / (m."reviewCount" - 1)
END,
"reviewCount" = m."reviewCount" - 1
FROM movie AS m INNER JOIN (
SELECT "userId", "movieId", score
FROM review) r
ON m.id = r."movieId"
WHERE r."userId" = $1;
However, when I run it, I get the same value across all my rows rather than accounting for the score of each review. Is there something I'm doing wrong or a better way to run this query?
As documented in the manual you should not repeat the target table of an UPDATE in the FROM clause.
There is also no need for a derived table ("sub-query") to get the values from the review table:
UPDATE movie m
SET score = CASE
WHEN m."reviewCount" = 1 THEN 0
ELSE ((m.score * m."reviewCount") - r.score) / (m."reviewCount" - 1)
END,
"reviewCount" = m."reviewCount" - 1
FROM review r
WHERE m.id = r."movieId" --<< this is the join between the two tables
AND r."userId" = $1;
This assumes that each user only reviews each movie once (i.e. one row per userid/movied combination in the review table). If that is not the case, the outcome is not predictable.
Last line should have
ON m.id = r."movieId"
WHERE movie.id = r."movieId" AND r."userId" = $1;
rather than m.id = r."movieId"

PostgreSQL update table with average vale from another table

I have tried this three different ways and PostgreSQL still doesn't like that I am "aggregate functions are not allowed in UPDATE". I am not sure how to this without doing that. Can someone point me in the right direction to populate my average Wage field with my average wages?
Attempt 1:
UPDATE public."JobCategory"
SET "AverageWage" = round(avg("Wage"), 4)
FROM public."Employees" WERE "Wage" > 0
Attempt 2:
UPDATE public."JobCategory" c
INNER JOIN (
SELECT round(avg("Wage"), 4) as average
FROM public."Employees"
) x ON c."Index" = x."JobIndex"
SET c."AverageWage" = x.average
Attempt 3:
UPDATE public."JobCategory" AS v
SET "AverageWage" = s.round(avg("Wage"), 4)
FROM public."Employees" AS s
WHERE v."Index" = s."JobIndex"
You can do this with a subquery:
WITH subq AS (
SELECT round(avg(Wage), 4) as average
FROM public.Employees
)
UPDATE public.JobCategory jc
SET AverageWage = subq.average
FROM subq

Variable number of arguments to sql.Query

I'm trying to pass a variable number of arguments to db.Query() in Go. I'm doing something like:
var values []interface{}
query := []string{"SELECT * FROM table"}
sep := "WHERE"
if ... {
values = append(values, something)
query = append(query, fmt.Sprintf(` %s field_a=$%d`, sep, len(values))
sep = "AND"
}
if ... {
values = append(values, something)
query = append(query, fmt.Sprintf(` %s field_b=$%d`, sep, len(values))
sep = "AND"
}
// Add an arbitrary number of conditional arguments...
rows, err := db.Query(strings.Join(query, " "), values...)
The query looks fine, and the values are all there, but I'm not getting anything back from the query. Some of the values are integers, some strings. When I manually try the exact same query (copy/paste) in psql, I substituting the actual values for $1, $2, etc, I get correct results. What am I missing?
Edit
Here's an example of what the final query (the result of strings.Join()) should look like:
SELECT
table1.field1, table1.field2, table1.field3,
table2.field4, table2.field6, table2.field6
FROM table1, table2
WHERE
table1.field2=$1 AND
table2.field3 IN ($2, $3)
When I try:
SELECT
table1.field1, table1.field2, table1.field3,
table2.field4, table2.field6, table2.field6
FROM table1, table2
WHERE
table1.field2='something' AND
table2.field3 IN (40, 50)
from psql, it works fine. When I call:
var values []interface{}
values = append(values, "something")
values = append(values, 40)
values = append(values, 50)
db.Query(`SELECT
table1.field1, table1.field2, table1.field3,
table2.field4, table2.field6, table2.field6
FROM table1, table2
WHERE
table1.field2=$1 AND
table2.field3 IN ($2, $3)`, values...)
from Go, I get a Rows object that returns false the first time rows.Next() is called, and a nil error.

Insert POINT into postgres database

I need to insert/update a point column type in postgres database.
I'm using node-postgres
The script generated using POSTGRES admin panel shows the update query as
UPDATE public.places SET id=?, user_id=?, business_name=?, alternate_name=?, primary_category=?, categories=?, description=?, address=?, city=?, state=?, country=?, zip=?, point WHERE <condition>;
How do I achieve point from latitude and longitude?
I have seen couple of answers using POSTGIS, but could not get it working.
In the documentation of POSTGRES (https://www.postgresql.org/docs/9.2/static/xfunc-sql.html) it is mentioned we can use point '(2,1)', but this does not work with pg query.
What I have now :
var config = {
user: 'postgres',
database: 'PGDATABASE',
password: 'PGPASSWORD!',
host: 'localhost',
port: 5432,
max: 10,
idleTimeoutMillis: 30000
};
And the update part :
app.post('/updatePlaces', function(req, res, next) {
console.log("Update");
console.log(req.body.places);
pool.query('UPDATE places SET address = $1, alternate_name = $2, business_name = $3, categories = $4, city = $5, country = $6, description = $7, point = $8, primary_category = $9, state = $10, zip = $11', [req.body.places.address, req.body.places.alternate_name, req.body.places.business_name, req.body.places.categories, req.body.places.city, req.body.places.country, req.body.places.description, (req.body.places.point.x, req.body.places.point.y), req.body.places.primary_category, req.body.places.state, req.body.places.zip], function(err, result) {
if(err) {
console.log(err);
return err;
}
res.send(result.rows[0]);
});
});
Tried many different ways for passing point :
(req.body.places.point.x, req.body.places.point.y)
point(req.body.places.point.x, req.body.places.point.y)
point '(2,1)'
All the above throws error. Do I need to use POSTGIS?
This works if you write SQL "directly":
CREATE TEMP TABLE x(p point) ;
INSERT INTO x VALUES ('(1,2)');
INSERT INTO x VALUES (point(3, 4));
SELECT * FROM x ;
Results
(1,2)
(3,4)
After couple of combinations, found out this works.!!
( '(' + req.body.places.point.x + ',' + req.body.places.point.y +')' )
Posting as answer if someone is trying to do this just using node-postgres.
So you can use single-quoted points: insert into x values ( '(1,2)' );
But using insert into x values (point(1,2)); in the query does not work.
I recently came across a similar problem using node-postgres when inserting into a postgis database with a geography (point) column. My solution was to use:
pool.query("INSERT INTO table (name, geography) VALUES ($1, ST_SetSRID(ST_POINT($2, $3), 4326))",
[req.body.name, req.body.lat, req.body.lng ]);
Current versions (Postgres 12, pg 8) should work simply use Postgres's POINT function to set a point column value.
Example:
export async function setPoint(client, x, y, id) {
const sql = `UPDATE table_name SET my_point = POINT($1,$2) WHERE id = $3 RETURNING my_point`;
const result = await client.query(sql, [x, y, id]);
return result.rows[0];
}
await setPoint(client, 10, 20, 5);
Result:
{x: 10.0, y: 20.0}
If you are using pg-promise, then custom types can be formatted automatically, see Custom Type Formatting.
You can introduce your own type like this:
function Point(x, y) {
this.x = x;
this.y = y;
// Custom Type Formatting:
this._rawDBType = true; // to make the type return the string without escaping it;
this.formatDBType = function () {
return 'ST_MakePoint(' + this.x + ',' + this.y + ')';
};
}
At some point you would create your objects:
var p = new Point(11, 22);
And then you can use such variables as regular types:
db.query('INSERT INTO places(place) VALUES(ST_SetSRID($1, 4326))', [p]);
See also: Geometry Constructors.

Returning distinct columns from left outer join in db2

SELECT
nzy.NZPYYD
,nzy.NZZSYG
,nzy.NZJRYG
,acn.ANITCD
FROM
ACNTRA acn
LEFT OUTER JOIN NZYTFL nzy
ON (
nzy.NZCNO1 = acn.ANCNO1
AND nzy.NZCNO2 = acn.ANCNO2
AND nzy.NZCNO3 = acn.ANCNO3
AND nzy.NZCNO4 = acn.ANCNO4
AND nzy.NZCNO5 = acn.ANCNO5
AND nzy.NZSLKI = acn.ANSLKI
AND nzy.NZDLTM = ''
)
WHERE
acn.ANDLTM = ''
AND acn.ANTKCD = '1029'
AND nzy.NZTXKB = 1
The problem here is it gives 2 rows result.I want to get one unique row from the result of left outer join .Any help?
If both rows are identical, try
SELECT DISTINCT
nzy.NZPYYD
,nzy.NZZSYG
,nzy.NZJRYG
,acn.ANITCD
If not, you can try to SUM(), CONCAT(), MAX() or whatever the column with different values.
Difficult to be more precise without a sample output.