"FOR UPDATE is not allowed with aggregate functions" in PostgreSQL - sql

Here is pseudo code for what I'm trying to do:
rate_count = SELECT COUNT(id) FROM job WHERE last_processed_at >= ?
current_limit = rate_limit - rate_count
if current_limit > 0
UPDATE job SET state='processing'
WHERE id IN(
SELECT id FROM job
WHERE state='pending'
LIMIT :current_limit
)
I have it working except for concurrency issues. When run from multiple sessions at the same time, both sessions SELECT and therefore update the same stuff :(
I'm able to get the 2nd query atomic by adding FOR UPDATE in it's SELECT subquery. But I can't add FOR UPDATE to the first query because FOR UPDATE isn't allowed with aggregate functions
How can I make this piece an atomic transaction?

You can do FOR UPDATE within a subquery:
rate_count := COUNT(id)
FROM (
SELECT id FROM job
WHERE last_processed_at >= ? FOR UPDATE
) a;
You can also do this whole thing in a single query:
UPDATE job SET state='processing'
WHERE id IN (
SELECT id FROM job
WHERE state='pending'
LIMIT (SELECT GREATEST(0, rate_limit - COUNT(id))
FROM (SELECT id FROM job
WHERE last_processed_at >= ? FOR UPDATE) a
)
)

I got the same error below:
ERROR: FOR UPDATE is not allowed with aggregate functions
Because I use count() and FOR UPDATE as shown below:
SELECT count(*) FROM person FOR UPDATE;
So, I changed the query above to one of 2 queries below:
SELECT count(*) FROM (SELECT * FROM person FOR UPDATE) AS result;
WITH result AS (SELECT * FROM person FOR UPDATE) SELECT count(*) FROM result;
Then, I could use count() and FOR UPDATE together:
count
-------
7
(1 row)

Related

In clause of a select but with more than one value to look for

I have a set of results that I query with connect by prior, now I need to check in the where clause of a query if ONE of those results is IN some other set of values(a select * from another table). I'm trying to use 'IN' but I think that that only works when I have one unique value to check for, and not a group of values.
SELECT COUNT('X')
INTO V_COUNT
FROM SIC_NEA_CATFRU
WHERE (SELECT cod_nivel_estr_art
FROM niveles_estr_art
CONNECT BY PRIOR cod_nivel_estr_art_P = cod_nivel_estr_art
START WITH cod_nivel_estr_art = V_COD_NIVEL_eSTR_ART) IN ( SELECT COD_NIVEL_eSTR_ART FROM SIC_NEA_CATFRU);
Would the intersect set operator do? Something like this: first query is your hierarchical query, while the second returns values from the sic_nea_catfru table. If there are any matches, you'll know how many:
SELECT COUNT (*)
INTO v_count
FROM ( SELECT cod_nivel_estr_art
FROM niveles_estr_art
CONNECT BY PRIOR cod_nivel_estr_art_p = cod_nivel_estr_art
START WITH cod_nivel_estr_art = v_cod_nivel_estr_art
INTERSECT
SELECT cod_nivel_estr_art FROM sic_nea_catfru)

Update Query using a Subquery to mark Duplicates

I have a Table that regularly gets Duplicate values added in. A simple fix would be to just add an extra column for me to check which has duplicates and remove accordingly. My Subquery Select statement works on its own, but not when I'm placing it as part of the Update Statement. I am using SSMS v18.7.1 and utilizing the latest SQL DB engine (I believe 2019 Express).Sample Data done with a Group By Query I understand that Update & Group By don't particularly mix well hence why I thought I could use a subquery to perform the requested action. Ideally I would also like to remove these duplicates, but there are other variables such as ApptDate & ActualDelivery Columns; However my only request is to set the Dupchecks to Yes when appropriate and then I will work on the logic for the Deletions subsequently.
Update a
Set Dupcheck = 'Yes'
from [Local DB].[dbo].[Test] a
where (
Select
ID,
count(*) as Count
From [Local DB].[dbo].[Test]
group by UID
having count(*) > 1)
You appear to be using SQL Server. I would suggest an updatable CTE:
with toupdate as
select t.*, count(*) over (partition by uid) as cnt
from [Local DB].[dbo].[Test] t
)
update toupdate
set Dupcheck = 'Yes'
where cnt > 1;
Note: If you want all but one of the rows to have the flag set, then use row_number() rather than count(*).
I think you need to use IN
Update a Set Dupcheck = Yes from [Local DB].[dbo].[Test] a where a. ID in ( Select ID From [Local DB].[dbo].[Test] group by UID having count(*) > 1)
Looking at your query it seems like you want to update all duplicate to be marked with flag as Yes.
You can use the following query to mark all the duplicates as yes:
Update test t
Set t.Dupcheck = 'Yes'
Where exists
(select 1 from Test tt
where t.uid = tt.uid
And t.id <> tt.id);
If you want to mark all except one record as duplicate then you can use > or < instead of <> in exists clause like And t.id > tt.id

ORA-01427: single-row subquery returns more than one row error comes after fetching some records

My query is fetching records in sql developer. But when i run it from ksh file and spool it, partial records get generates and error comes. How can I find out the data for which this error is coming?
Query is:
select im.item as "ITEM",
(select val.uda_value_desc
from uda_values val,UDA_ITEM_LOV lov
where lov.item=im.item
and lov.uda_id=val.uda_id
and lov.UDA_VALUE=val.UDA_VALUE
and val.uda_id=3) as "SERIE",
(select val.uda_value_desc
from uda_values val, UDA_ITEM_date lov
where lov.item=im.item
and lov.uda_id=val.uda_id
and val.uda_id=20) as "UDA_DATE"
from ahl_rumm_prod_item_master im;
table ahl_rumm_prod_item_master has 313535 records.
One way that make minimal changes to your code is to change your correlated sub-queries to SELECT COUNT( ... ) FROM ... and then filter out the rows where something other than one result is returned:
SELECT *
FROM (
select im.item as "ITEM",
(select COUNT( val.uda_value_desc )
from uda_values val,
UDA_ITEM_LOV lov
where lov.item=im.item
and lov.uda_id=val.uda_id
and lov.UDA_VALUE=val.UDA_VALUE
and val.uda_id=3
) as "SERIE",
(select COUNT( val.uda_value_desc )
from uda_values val,
UDA_ITEM_date lov
where lov.item=im.item
and lov.uda_id=val.uda_id
and val.uda_id=20
) as "UDA_DATE"
from ahl_rumm_prod_item_master im
)
WHERE SERIE <> 1
OR UDA_DATE <> 1;
This will tell you the items where errors are occurring and you can investigate further.
One or both of your inner query return more than 1 row. therefore It cannot be set as a column value. What you should do is add some conditions to the inner queries to make sure that they return just one row

Why COUNT(*) is equal to 1 without FROM clause? [duplicate]

This question already has an answer here:
Why does "select count(*)" from nothing return 1
(1 answer)
Closed 7 years ago.
for a quick check I used a query
select COUNT(*) LargeTable
and was surprized to see
LargeTable
-----------
1
seconds later I realized my mistake, made it
select COUNT(*) from LargeTable
and got expected result
(No column name)
-----------
1.000.000+
but now I don't understand why COUNT(*) returned 1
it happens if I do select COUNT(*) or declare #x int = COUNT(*); select #x
another case
declare #EmptyTable table ( Value int )
select COUNT(*) from #EmptyTable
returns
(No column name)
-----------
0
I did't find explanation in SQL standard (http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt, online source is given here https://stackoverflow.com/a/8949764/1506454)
why COUNT(*) returns 1?
In SQL Server a SELECT without a FROM clause works as though it operates against a single row table.
This is not standard SQL. Other RDBMSs provide a utility DUAL table with a single row.
So this would be treated effectively the same as
SELECT COUNT(*) AS LargeTable
FROM DUAL
A related Connect Item discussing
SELECT 'test'
WHERE EXISTS (SELECT *)
is https://connect.microsoft.com/SQLServer/feedback/details/671475/select-test-where-exists-select
Because without the FROM clause DBMS cannot know [LargeTable] is a table. You tricked it in guessing it's a COLUMN NAME alias
You can try it and see
select count(*) 'eklmnjdklfgm'
select count(*) eklmnjdklfgm
select count(*) [eklmnjdklfgm]
select count(*)
The first 3 examples returns eklmnjdklfgm as column name
Count(*) returned 1 because your sentence is not of SQL.
1) In the first sentences, you accounts a table empty, with an only row, since you don't put to which table want to access.
2) In the second case:
declare #EmptyTable table ( Value int )
select COUNT(*) from #EmptyTable
returns
(No column name)
-----------
0
You put the variable, but don't determine to which table implement him. For this, do a count and go out 0.

SQL get rows matching ALL conditions

I would like to retrieve all rows matching a set of conditions on the same column. But I would like the rows only if ALL the conditions are good, and no row if only one condition fails.
For example, taking this table:
|id|name|
---------
|1 |toto|
|2 |tata|
I would like to be able to request if "tata" && "toto" are in this table. But when asking if "tata" and "tuto" are in, I would like an empty response if one of argument is in not in the table, for example asking if "toto" && "tutu" are included in the table.
How can I do that ?
Currently, I'am doing one query per argument, which is not very efficient. I tried several solutions including a subselect or a group+having, but no one is working like I want.
thanks for your support !
cheers
This isn't the most efficient way, but this query would work.
SELECT * FROM table_name
WHERE (name = 'toto' OR name = 'tata')
AND ( SELECT COUNT(*) FROM table_name WHERE name = 'toto') > 0
AND ( SELECT COUNT(*) FROM table_name WHERE name = 'tata') > 0
This is a little vague. If the names are unique, you could count the matching rows that match a where clause:
where name='toto' or name='tata'
If the count is 2, then you know both matched. If name is not unique you could potentially select the first ID (select top 1 id ...) that matches each in a union and count those with an outer select.
Even if you had an arbitrary number of names to match, you could create a stored procedure or code in whatever top-level language you are using to build the select statement.
SELECT 1 AS found FROM hehe
WHERE 1 IN (SELECT 1 FROM hehe WHERE name='tata')
AND 1 IN (SELECT 1 FROM hehe WHERE name='toto')
If name is unique you can simplify to:
SELECT *
FROM tbl
WHERE name IN ('toto', 'tata')
AND (SELECT count(*) FROM tbl WHERE name IN ('toto', 'tata')) > 1;
If it isn't:
SELECT *
FROM tbl
WHERE name IN ('toto', 'tata')
AND EXISTS (SELECT * FROM tbl WHERE name = 'toto')
AND EXISTS (SELECT * FROM tbl WHERE name = 'tata');
Or, in PostgreSQL, MySQL and possibly others:
SELECT *
FROM tbl
WHERE name IN ('toto', 'tata')
AND (SELECT count(DISTINCT name) FROM tbl WHERE name IN ('toto', 'tata')) > 1;