performing an update query with a select subquery returning either the same value for ALL of the records or ora-01427 error - sql

I need to update a column in one table with the results from a select sub-query (and they should ultimately be different). But When I do this, I either get the exact same number for the hundreds of records, or I get the ORA-01427: single row sub-query returns more than one row query. error.
Can you please take a look and see what it is that I am overlooking? (I could just be overlooking something simple for all I know)
UPDATE WD_PRODUCT_CLASS
SET CURRENT_CASES = ( WITH STUFF_COUNT AS
(
SELECT sum(CURRENT_DETAIL.COMBINED_QTY) AS TOTAL_CASES
FROM CURRENT_DETAIL, SKU_MAJORS, WD_PRODUCT_CLASS
WHERE CURRENT_DETAIL.LOC_ID =
&PARM_LOC_ID
AND CURRENT_DETAIL.INVEN_ID = SKU_MAJORS.INVEN_ID
AND WD_PRODUCT_CLASS.CATEGORY = SKU_MAJORS.CONT_DESC
GROUP BY WD_PRODUCT_CLASS.CATEGORY
)
(
SELECT SUM(Z.TOTAL_CASES) FROM STUFF_COUNT Z
)
);

Maybe you need someting like this:
UPDATE WD_PRODUCT_CLASS wpc
SET wpc.CURRENT_CASES = (
SELECT sum(cd.COMBINED_QTY)
FROM CURRENT_DETAIL cd join SKU_MAJORS sm ON cd.INVEN_ID = sm.INVEN_ID
WHERE cd.LOC_ID = &PARM_LOC_ID
AND sm.CONT_DESC = wpc.CATEGORY
)
WHERE 1=1; -- if you don't set a condition all the rows will be updated
Your query updates the table with the same values because you're using a not correlated subquery in the SET clause. This subquery don't depends on the parent query, so it's calculated only once.
I suppose you need a correlated subquery so I changed your update + removed some extra parts.

Related

Fetching and concatenate two rows value from two different table in SQL

I am unable to Fetch and concatenate two rows value from two different table in SQL.
Please see my query in attached photo.
Following query doesn't providing me the exact data
SELECT RequestNo+'::'+convert(varchar(200),(select count(RID)+1
from BDProjectProposal
Group by RID)) AS Number
FROM BDRequestorInfo
WHERE (RID = #RID)
Is there any way?
You should use a correlated subquery:
SELECT (RequestNo + '::' +
convert(varchar(200),
(select count(RID) + 1
from BDProjectProposal pp
where pp.RID = ri.RID)
)
)
) AS Number
FROM BDRequestorInfo ri
WHERE ri.RID = #RID;
Can you try below
SELECT RequestNo+'::'+convert(varchar(200),isnull((select count(RID)+1
from BDProjectProposal
Group by RID
having RID = #RID),1) AS Number
FROM BDRequestorInfo
WHERE (RID = #RID)
I only added WHERE clause (having RID = #RID) in Subselect to ensure that there will be only one value returning.
Your subselect returns a dataset causing possibly an SQL error
I modified above query and added ISNULL(....,1) for NULL returns with no rows for a given #RID

Updating a table row multiple values oracle 11g

I m struggling to update one column for a table with a sub query. I have a table where currently one of the values is null.
Currently I have:
UPDATE DW1_PURCHASES SET DW1_PURCHASES.TOTAL_AMT =
(
SELECT DW1_PURCHASES.QUANTITY * DW1_PRODUCTS.PRICE
FROM DW1_PURCHASES, DW1_PRODUCTS
WHERE DW1_PURCHASES.PRODUCT_ID = DW1_PRODUCTS.PRODUCT_ID
)
Although subquery returns data which I need to insert I get a error of single row subquery returns multiple rows.How do I basically shift subquery result to the table?
Thanks.
You don't have to JOINthe update table inside the sub-query. Just correlate the sub-query with update table
UPDATE DW1_PURCHASES
SET DW1_PURCHASES.TOTAL_AMT =
(
SELECT DW1_PURCHASES.QUANTITY * DW1_PRODUCTS.PRICE
FROM DW1_PRODUCTS
WHERE DW1_PURCHASES.PRODUCT_ID = DW1_PRODUCTS.PRODUCT_ID
)
Note : If your DW1_PRODUCTS table has duplicated PRODUCT_ID then even now there is a possibility to get the same error

What is the difference between the IN operator and = operator in SQL?

I am just learning SQL, and I'm wondering what the difference is between the following lines:
WHERE s.parent IN (SELECT l.parent .....)
versus
WHERE s.parent = (SELECT l.parent .....)
IN
will not generate an error if you have multiple results on the subquery. Allows to have more than one value in the result returned by the subquery.
=
will generate an error if you have more than one result on the subquery.
SQLFiddle Demo (IN vs =)
when you are using 'IN' it can compare multiple values....like
select * from tablename where student_name in('mari','sruthi','takudu')
but when you are using '=' you can't compare multiple values
select * from tablenamewhere student_name = 'sruthi'
i hope this is the right answer
The "IN" clause is also much much much much slower. If you have many results in the select portion of
IN (SELECT l.parent .....),
it will be extremely inefficient as it actually generates a separate select sql statement for each and every result within the select statement ... so if you return 'Cat', 'Dog', 'Cow'
it will essentially create a sql statement for each result... if you have 200 results... you get the full sql statement 200 times...takes forever... (This was as of a few years ago... maybe imporved by now... but it was horribly slow on big result sets.)
Much more efficient to do an inner join such as:
Select id, parent
from table1 as T
inner join (Select parent from table2) as T2 on T.parent = T2.parent
For future visitors.
Basically in case of equals (just remember that here we are talking like where a.name = b.name), each cell value from table 1 will be compared one by one to each cell value of all the rows from table 2, if it matches then that row will be selected (here that row will be selected means that row from table 1 and table 2) for the overall result set otherwise will not be selected.
Now, in case of IN, complete result set on the right side of the IN will be used for comparison, so its like each value from table 1 will be checked on whether this cell value is present in the complete result set of the IN, if it is present then that value will be shown for all the rows of the IN’s result set, so let say IN result set has 20 rows, so that cell value from table 1 will be present in overall result set 20 times (i.e. that particular cell value will have 20 rows).
For more clarity see below screen shot, notice below that how complete result set from the right of the IN (and NOT IN) is considered in the overall result set; whole emphasis is on the fact that in case comparison using =, matching row from second table is selected, while in case of IN complete result from the second table is selected.
In can match a value with more than one values, in other words it checks if a value is in the list of values so for e.g.
x in ('a', 'b', 'x') will return true result as x is in the the list of values
while = expects only one value, its as simple as
x = y returns false
and
x = x returns true
The general rule of thumb is:
The = expects a single value to compare with. Like this:
WHERE s.parent = 'father_name'
IN is extremely useful in scenarios where = cannot work i.e. scenarios where you need the comparison with multiple values.
WHERE s.parent IN ('father_name', 'mother_name', 'brother_name', 'sister_name')
Hope this is useful!!!
IN
This helps when a subquery returns more than one result.
=
This operator cannot handle more than one result.
Like in this example:
SQL>
Select LOC from dept where DEPTNO = (select DEPTNO from emp where
JOB='MANAGER');
Gives ERROR ORA-01427: single-row subquery returns more than one row
Instead use
SQL>
Select LOC from dept where DEPTNO in (select DEPTNO from emp
where JOB='MANAGER');
1) Sometimes = also used as comparison operator in case of joins which IN doesn't.
2) You can pass multiple values in the IN block which you can't do with =. For example,
SELECT * FROM [Products] where ProductID IN((select max(ProductID) from Products),
(select min(ProductID) from Products))
would work and provide you expected number of rows.However,
SELECT * FROM [Products] where ProductID = (select max(ProductID) from Products)
and ProductID =(select min(ProductID) from Products)
will provide you 'no result'. That means, in case subquery supposed to return multiple number of rows , in that case '=' isn't useful.

Guidance on using the WITH clause in SQL

I understand how to use the WITH clause for recursive queries (!!), but I'm having problems understanding its general use / power.
For example the following query updates one record whose id is determined by using a subquery returning the id of the first record by timestamp:
update global.prospect psp
set status=status||'*'
where psp.psp_id=(
select p2.psp_id
from global.prospect p2
where p2.status='new' or p2.status='reset'
order by p2.request_ts
limit 1 )
returning psp.*;
Would this be a good candidate for using a WITH wrapper instead of the relatively ugly sub-query? If so, why?
If there can be concurrent write access to involved tables, there are race conditions in the following queries. Consider:
Postgres UPDATE … LIMIT 1
Your example can use a CTE (common table expression), but it will give you nothing a subquery couldn't do:
WITH x AS (
SELECT psp_id
FROM global.prospect
WHERE status IN ('new', 'reset')
ORDER BY request_ts
LIMIT 1
)
UPDATE global.prospect psp
SET status = status || '*'
FROM x
WHERE psp.psp_id = x.psp_id
RETURNING psp.*;
The returned row will be the updated version.
If you want to insert the returned row into another table, that's where a WITH clause becomes essential:
WITH x AS (
SELECT psp_id
FROM global.prospect
WHERE status IN ('new', 'reset')
ORDER BY request_ts
LIMIT 1
)
, y AS (
UPDATE global.prospect psp
SET status = status || '*'
FROM x
WHERE psp.psp_id = x.psp_id
RETURNING psp.*
)
INSERT INTO z
SELECT *
FROM y;
Data-modifying queries using CTEs were added with PostgreSQL 9.1.
The manual about WITH queries (CTEs).
WITH lets you define "temporary tables" for use in a SELECT query. For example, I recently wrote a query like this, to calculate changes between two sets:
-- Let o be the set of old things, and n be the set of new things.
WITH o AS (SELECT * FROM things(OLD)),
n AS (SELECT * FROM things(NEW))
-- Select both the set of things whose value changed,
-- and the set of things in the old set but not in the new set.
SELECT o.key, n.value
FROM o
LEFT JOIN n ON o.key = n.key
WHERE o.value IS DISTINCT FROM n.value
UNION ALL
-- Select the set of things in the new set but not in the old set.
SELECT n.key, n.value
FROM o
RIGHT JOIN n ON o.key = n.key
WHERE o.key IS NULL;
By defining the "tables" o and n at the top, I was able to avoid repeating the expressions things(OLD) and things(NEW).
Sure, we could probably eliminate the UNION ALL using a FULL JOIN, but I wasn't able to do that in my particular case.
If I understand your query correctly, it does this:
Find the oldest row in global.prospect whose status is 'new' or 'reset'.
Mark it by adding an asterisk to its status
Return the row (including our tweak to status).
I don't think WITH will simplify anything in your case. It may be slightly more elegant to use a FROM clause, though:
update global.prospect psp
set status = status || '*'
from ( select psp_id
from global.prospect
where status = 'new' or status = 'reset'
order by request_ts
limit 1
) p2
where psp.psp_id = p2.psp_id
returning psp.*;
Untested. Let me know if it works.
It's pretty much exactly what you have already, except:
This can be easily extended to update multiple rows. In your version, which uses a subquery expression, the query would fail if the subquery were changed to yield multiple rows.
I did not alias global.prospect in the subquery, so it's a bit easier to read. Since this uses a FROM clause, you'll get an error if you accidentally reference the table being updated.
In your version, the subquery expression is encountered for every single item. Although PostgreSQL should optimize this and only evaluate the expression once, this optimization will go away if you accidentally reference a column in psp or add a volatile expression.

Insert into using Variables

I have the following SQL query:
Declare #Total_SysDown as int,
#Login_SysDown as int
Set #Total_SysDown = (SELECT SCHED_SYS_DOWN FROM AGT_SC AS S)
Set #Login_SysDown = (SELECT SYS_DOWN FROM AGT_AC AS A)
Insert Into dbo.DATA(DATE,ID,LNAME,FNAME,Total_SysDown,Login_SysDown)
Select C.DATE,C.ID,E.Last_Name,E.First_Name,#Total_SysDown #Login_SysDown
From dbo.AGT as C Inner Join dbo.EMP as E ON C.ID = E.ID
Group by C.ID,C.DATE,E.Last_Name,E.First_Name
This or just the variables with the Select statement gives me an error of Subquery returned than 1 value. From what I understand, this means that I should be inserting one record at a time, but I am unsure how to do this. Is there a while statement I should be putting in, or are my variables actually hindering me in the first place?
At least one of the queries:
(SELECT SCHED_SYS_DOWN FROM AGT_SC AS S)
(SELECT SYS_DOWN FROM AGT_AC AS A)
returns more than 1 row, so you cannot assign it to a scalar variable.
As a temporary solution you can do SELECT TOP 1 to make sure each query returns at most one row.
I don't think the problem is with your INSERT statement at all.
Your problem is in the SET Statements. The SELECT SCHED_SYS_DOWN FROM AGT_SC AS S statement or the other statement is returning more than one value.
When you use SET you are assigning ONE value to the variable. Your SELECT is returning multiple values. Change your query to return only one row.
You're receiving this error because your subqueries return more than one record:
Set #Total_SysDown = (SELECT SCHED_SYS_DOWN FROM AGT_SC AS S)
Set #Login_SysDown = (SELECT SYS_DOWN FROM AGT_AC AS A)
To use variables here, you will need to ensure that only one record is returned from the query, either by using a WHERE clause, TOP 1, or something else. I can't tell for sure by your example, but it sounds like you should be joining those tables to your SELECT query.
SELECT ...
FROM dbo.AGT agt
INNER JOIN AGT_SC sc
ON sc.<joining column> = <joining table>.<joining column>
INNER JOIN AGT_AC ac
ON ac.<joining column> = <joining table>.<joining column>