SQL: delete only if subquery (containing 0, 1, or more rows) equals scalar value - sql

Description
I'm trying to do something like:
DELETE FROM ...
-- `<my_id>` is any given long scalar value
WHERE <my_id> = (SELECT id FROM ... WHERE ...);
That syntax is not entirely correct. If the select subquery contains more than one row, then we get an error like:
Scalar subquery contains more than one row
However, my intended purpose is indeed:
if the subquery returns exactly 1 value equal to the scalar value (<my_id>) ==> delete
else (0, or 2 or more values, or 1 non-equal value) ==> do not delete (ignore)
Question
I do not want neither IN nor EXISTS. I need rather something like "equals", which can compare a scalar value against possibly multi-valued rows.
What is the syntax in SQL for that?
Stack
In particular, I'm testing this with H2.

One method is:
DELETE FROM ...
WHERE <my_id> IN (SELECT id FROM ... WHERE ...) AND
(SELECT COUNT(*) FROM . . . WHERE . . .) = 1;
However, this is more simply written as:
DELETE FROM ...
WHERE <my_id> = (SELECT MAX(id)
FROM . . .
WHERE . .
HAVING COUNT(*) = 1
) ;
If the count is not 1, then the subquery returns nothing and there is no match (so nothing is deleted).

Related

AGE question: WITH clause doesn't work with MATCH/WITH

If I have the following query
select * from cypher('agload_test_graph', $$ match (n) with n where n.name='A' return n $$) as (sp agtype)
then n.name='A' doesn't work.
but if I remove with clause, then it works.
select * from cypher('agload_test_graph', $$ match (n) where n.name='A' return n $$) as (sp agtype)
I tried the example query in age document.
SELECT *
FROM cypher('graph_name', $$
MATCH (david {name: 'David'})-[]-(otherPerson)-[]->()
WITH otherPerson, count(*) AS foaf
WHERE foaf > 1RETURN otherPerson.name
RETURN otherPerson.name
$$) as (name agtype);
The problem with your first query is that the WHERE clause is not applied to the n variable because the WHERE clause is used before the WITH clause.  By WITHclause, a new variable that is a subset of the previous variable is created in Cypher. Any filters added after the the WITH clause will be applied to this new variable rather than the original variable. By deleting the WITH clause from your second query, the where clause is being applied directly to the n variable, which is what you want.
The example query in the age document you provided is a legitimate Cypher query. It uses MATCH to find a node with the name "David", then it uses  WITH clause to create a new variable otherPerson, which is the set of nodes nodes connected to the "David" node, and then uses  count(*) function to count the number of nodes connected to the "David" node. After this it uses the WHERE clause to filter otherPerson nodes where the count of their connections is greater than 1 and return their names.
The where clause after with is ignored. This is one of the issues in Apache Age which is opened on github. Issue Page

ORA-01427: single-row subquery returns more than one row with update

I have an update statement, at first it worked with some data, but after a while I've been having issues with some regiters , I got the following error:
ORA-01427: single-row subquery returns more than one row . I thought it was the IN because the query might return repeated IDs, so I put a DISTINCT but I kept getting the same error, does anyone have any idea what could it be?
The query below:
UPDATE
TABLE_DETAIL DET
SET
DET.ACCEPTED=?,
DET.VALUE_ACCEPTED=(SELECT RES.VALUE FROM TABLE_RES RES WHERE RES.MYT=DET.MYT AND RES.RAD=DET.RAD AND RES.ITEM=DET.ITEM),
WHERE
DET.ID
IN (
SELECT
DISTINCT DET_I.ID
FROM TABLE_DETAIL DET_I, TABLE_RES RES_I, TABLE_REC REC
WHERE
DET_I.MYT = ?
AND
REC.TEMP
NOT IN (
SELECT DISTINCT TEMP
FROM
TABLE_IMAGES WHERE CODE=?)
AND RES_I.USER = ?
)
You don't need SELECT DISTINCT with IN.
The proximal cause of your problem is this code:
DET.VALUE_ACCEPTED = (SELECT RES.VALUE FROM TABLE_RES RES WHERE RES.MYT=DET.MYT AND RES.RAD=DET.RAD AND RES.ITEM=DET.ITEM),
It isn't really possible to say how to fix it. The error code seems pretty clear: More than one row is returned by this query.
You can add WHERE rownum = 1 if you want an arbitrary value. Or an aggregation function in the SELECT.

postgres 9.2 counting the number of statuses by ordinal

suppose i have this result set:
Location|Company|Account Number|First Check Date|First Status|Second Check Date|Second Status|Third Check Date|Third Status|Fourth Check Date|Fourth Status|Fifth Check Date|Fifth Status
Westeros|Acme Corp.|1014541|8/23/2018|Denied||||||||
Westeros|Acme Corp.|1014544|8/23/2018|Pending||||||||
Westeros|Acme Corp.|1014561|8/23/2018|Pending||||||||
Westeros|Sirius Cybernetics Corp|1014562|8/22/2018|Finalized||||||||
Westeros|Sirius Cybernetics Corp|1014573|8/23/2018|Pending||||||||
Westeros|MomCorp|1014579|8/22/2018|Denied||||||||
Dorne|MomCorp|1018984|8/20/2018|Pending||||||||
Dorne|Sirius Cybernetics Corp|1019017|8/22/2018|Pending||||||||
The North|MomCorp|1033591|8/16/2018|Pending||||||||
The North|MomCorp|1033910|8/16/2018|Not Found||||||||
The North|Amerigroup|1033964|8/16/2018|Partial Payment||||||||
The North|MomCorp|1034036|8/22/2018|Paid||||||||
The North|MomCorp|1034041|8/23/2018|Partial Payment||||||||
Iron Islands|AG White|1175033|8/22/2018|Pending||||||||
Stormlands|foobar|1220179|8/14/2018|Not Found|8/21/2018|Not Found||||||
which is returnd from a query that can be boiled down to
select Location,Company,Account Number,First Check Date,First Status,Second Check Date,Second Status,Third Check Date,Third Status,Fourth Check Date,Fourth Status,Fifth Check Date,Fifth Status
from subselect as results
what i an trying to do is generate a count of elements in each status column.
for example the first status count would be 15, the second would be 1 and the rest are 0.
I have tried doing this with over() and using a sum(case when 'first_status' then1 else 0 end) but nothing seems to give correct results.
any suggestions on how to accomplish this?
Assuming the missing values are NULL, you can do:
select r.*,
count(status1) over () as status1_cnt,
count(status2) over () as status2_cnt,
. . .
from subselect as results
However, the empty statuses might be something else, such as ''. If so, a simple approach is:
select r.*,
count(nullif(status1, '')) over () as status1_cnt,
count(nullif(status2, '')) over () as status2_cnt,
. . .
from subselect as results

num_rows in postgres always return 1

I'm trying to do a SELECT COUNT(*) with Postgres.
What I need: Catch the rows affected by the query. It's a school system. If the student is not registered, do something (if).
What I tried:
$query = pg_query("SELECT COUNT(*) FROM inscritossimulado
WHERE codigo_da_escola = '".$CodEscola."'
AND codigo_do_simulado = '".$simulado."'
AND codigo_do_aluno = '".$aluno."'");
if(pg_num_rows($query) == 0)
{
echo "Error you're not registered!";
}
else
{
echo "Hello!";
}
Note: The student in question IS NOT REGISTERED, but the result is always 1 and not 0.
For some reason, when I "show" the query, the result is: "Resource id #21". But, I look many times in the table, and the user is not there.
You are counting the number of rows in the answer, and your query always returns a single line.
Your query says: return one row giving the number of students matching my criteria. If no one matches, you will get back one row with the value 0. If you have 7 people matching, you will get back one row with the value 7.
If you change your query to select * from ... you will get the right answer from pg_num_rows().
Actually, don't count at all. You don't need the count. Just check for existence, which is proven if a single row qualifies:
$query = pg_query(
'SELECT 1
FROM inscritossimulado
WHERE codigo_da_escola = $$' . $CodEscola . '$$
AND codigo_do_simulado = $$' . $simulado. '$$
AND codigo_do_aluno = $$' . $aluno . '$$
LIMIT 1');
Returns 1 row if found, else no row.
Using dollar-quoting in the SQL code, so we can use the safer and faster single quotes in PHP (I presume).
The problem with the aggregate function count() (besides being more expensive) is that it always returns a row - with the value 0 if no rows qualify.
But this still stinks. Don't use string concatenation, which is an open invitation for SQL injection. Rather use prepared statements ... Check out PDO ...

sqlite3 UPDATE generating nulls

I'm trying to transition from MySQL to SQLIte3 and running into an update problem. I'm using SQLite 3.6.20 on redhat.
My first line of code behaves normally
update atv_covar set noncomp= 2;
All values for noncomp (in the rightmost column) are appropriately set to 2.
select * from atv_covar;
A5202|S182|2
A5202|S183|2
A5202|S184|2
It is the second line of code that gives me problems:
update atv_covar
set noncomp= (select 1 from f4003 where
atv_covar.study = f4003.study and
atv_covar.rpid = f4003.rpid and
(rsoffrx="81" or rsoffrx="77"));
It runs without generating errors and appropriately sets atv_covar.noncomp to 1 where it matches the SELECT statement. The problem is that it changes atv_covar.noncomp for the non-matching rows to null, where I want it to keep them as 2.
select * from atv_covar;
A5202|S182|
A5202|S183|1
A5202|S184|
Any help would be welcome.
#Dan, the problem with your query is not specific to SQLite; you are updating all rows of atv_covar, but not all of them have correspondence in f4003, so these default to NULL. You should filter the update or provide a default value.
The following statement sets 1 only to the rows that macth the filtering condition:
UPDATE atv_covar
SET noncomp = 1
WHERE EXISTS (
SELECT 'x'
FROM f4003
WHERE atv_covar.study = f4003.study
AND atv_covar.rpid = f4003.rpid
AND (rsoffrx="81" or rsoffrx="77")
);
The following statement sets 1 or 2 for all rows of noncomp, depending on the filtering match (use this instead of two updates):
UPDATE atv_covar
SET noncomp = COALESCE((
SELECT 1
FROM f4003
WHERE atv_covar.study = f4003.study
AND atv_covar.rpid = f4003.rpid
AND (rsoffrx="81" or rsoffrx="77")
), 2);