sql - Update more rows than I expected - sql

I'm trying to update a part of my table. If I do a select statement, I find 17 ocurrences, but when I update it, it updates 997 ocurrences. I only want to update the 17 ocurrences. This is my code:
update proc_try k set detail = (
select jobs from
(
with
a ( nameHost ) as (
select b.nameHost
from definition a ,schema.nodes b
where b.nameHost = a.idNode or b.nodeid=a.idNode
and nodetype not like 'R'
group by b.nameHost
having sum(1 + lengthb(nameJob)) - 1 > 4000
)
select nameHost, 'TOOLONG' as jobs
from a
UNION ALL
select p.nameHost, listagg(p.nameJob,',') within group (order by p.nameJob) as jobs
from
(
select distinct b.nameJob, a.nameHost
from definition b
right join schema.nodes a
on b.idNode in (a.nodeid,a.nameHost) and
b.application not like '#NOTINCLUDE'
where a.nameHost not in (select * from a) and nodetype not like 'R'
--b.application not like '#NOTINCLUDE'
) p
group by p.nameHost) random
where k.nameHost=random.nameHost);
Could you help me please?

You can generally convert a complex update into a merge:
merge into proc_try k
using
( select jobs
from ( with a(namehost) as
( select b.namehost
from definition a
join schema.nodes b
on b.namehost = a.idnode
or (b.nodeid = a.idnode and nodetype <> 'R')
group by b.namehost
having sum(1 + lengthb(namejob)) - 1 > 4000 )
select namehost
, 'TOOLONG' as jobs
from a
union all
select p.namehost
, listagg(p.namejob, ',') within group(order by p.namejob) as jobs
from ( select distinct
b.namejob, a.namehost
from schema.nodes a
left join definition b
on b.idnode in (a.nodeid, a.namehost)
and b.application not like '#NOTINCLUDE'
where a.namehost not in (select * from a)
and nodetype not like 'R'
) p
group by p.namehost
) random
) new_jobs
on (k.namehost = new_jobs.namehost)
when matched then update set k.detail = new_jobs.jobs;
This is untested as I don't have your tables or sample data.
Edit: Looks like we can simplify it a bit, to this:
merge into proc_try k
using
( with overlength (namehost) as
( select n.namehost
from definition d
join schema.nodes n
on n.namehost = d.idnode
or (n.nodeid = d.idnode and nodetype <> 'R')
group by n.namehost
having sum(1 + lengthb(n.namejob)) - 1 > 4000 )
select o.namehost, 'TOOLONG' as jobs
from overlength o
union all
select sd.namehost
, listagg(sd.namejob, ',') within group(order by sd.namejob) as jobs
from ( select distinct d.namejob, n.namehost
from schema.nodes n
left join definition d
on d.idnode in (n.nodeid, n.namehost)
and d.application not like '#NOTINCLUDE'
where n.namehost not in (select o.namehost from overlength o)
and n.nodetype not like 'R'
) sd
group by sd.namehost
) new_jobs
on (new_jobs.namehost = k.namehost)
when matched then update set k.detail = new_jobs.jobs;
I still can't see what
sum(1 + lengthb(namejob)) - 1
is meant to do, though. It looks like that could be simplified to
sum(lengthb(namejob))

Related

Update statement SQL with CTE

I have a common table expression that I am using trying to use as an update statement. The only reason for the CTE is so I can use a where clause to filter by CredCount. I would like to use that where clause to update only records that match in this case CredCount of 2. However, I am having trouble with the update part of the query.
Members_id
Credentials
CredCount
members_amountdue
1
CMA, CPR
2
0
2
CMA, CPR
2
0
3
CMA, CPR
2
0
Here is the query for that
WITH CTE AS (
SELECT members_id, members_amountdue, [Credentials], LEN([Credentials]) - LEN(REPLACE([Credentials], ',', '')) + 1 AS CredCount
FROM (
SELECT DISTINCT mem1.members_id, mem1.members_amountdue,
STUFF(
(SELECT DISTINCT ', ' + credentials_code
FROM members AS mem JOIN
members_credentials AS mc ON mc.members_id = mem.members_id JOIN
credentials AS c ON c.credentials_id = mc.credentials_id
WHERE mem.[members_id] = mem1.[members_id]
FOR XML PATH ('')), 1, 1, '') AS [Credentials]
FROM members AS mem1 JOIN
members_status as ms on ms.members_status_id = mem1.members_status_id
) AS derived),
CTE2 AS ( SELECT members_id
FROM members)
SELECT CTE.members_id, CTE.Credentials, CTE.CredCount, (CTE.members_amountdue + 25) as NewPriceTotal
FROM CTE JOIN
CTE2 ON CTE.members_id = CTE2.members_id
WHERE CTE.CredCount = 2
With the update statement I was looking at an example provided here at Update records in table from CTE so I added the update statement to the bottom of the query
WITH CTE AS (
SELECT members_id, members_amountdue, [Credentials], LEN([Credentials]) - LEN(REPLACE([Credentials], ',', '')) + 1 AS CredCount
FROM (
SELECT DISTINCT mem1.members_id, mem1.members_amountdue,
STUFF(
(SELECT DISTINCT ', ' + credentials_code
FROM members AS mem JOIN
members_credentials AS mc ON mc.members_id = mem.members_id JOIN
credentials AS c ON c.credentials_id = mc.credentials_id
WHERE mem.[members_id] = mem1.[members_id]
FOR XML PATH ('')), 1, 1, '') AS [Credentials]
FROM members AS mem1 JOIN
members_status as ms on ms.members_status_id = mem1.members_status_id
) AS derived),
CTE2 AS ( SELECT members_id
FROM members)
SELECT CTE.members_id, CTE.Credentials, CTE.CredCount, (CTE.members_amountdue + 25) as NewPriceTotal
FROM CTE JOIN
CTE2 ON CTE.members_id = CTE2.members_id
WHERE CTE.CredCount = 2
UPDATE members
set members_amountdue = NewPriceTotal
When I add the update statement I get an error for invalid column name for 'NewPriceTotal'. I know I need the column to match in order for the update to work but I'm not sure why it's invalid.
Members_id
Credentials
CredCount
NewPriceTotal
1
CMA, CPR
2
25
2
CMA, CPR
2
25
3
CMA, CPR
2
25
I would like for the NewPriceTotal to be set at the members_amountdue from the members table but I'm not sure where I made the wrong turn at. Any help, comments or feedback is greatly appreciated.
Try this:
....
CTE2 AS (
SELECT members_id
FROM members
), CTE3 AS (
SELECT CTE.members_id, CTE.Credentials, CTE.CredCount,
CTE.members_amountdue,
(CTE.members_amountdue + 25) as NewPriceTotal
FROM CTE JOIN CTE2 ON CTE.members_id = CTE2.members_id
WHERE CTE.CredCount = 2)
UPDATE CTE3
SET members_amountdue = NewPriceTotal

Oracle sql join against extracted values

I am looking to reconcile data from 2 different tables where I need to carry out a concatenation and substr to create columns that I can use to carry out a match against.The following separate queries reflect the select statements from each table that produces the matching values that reflect sitenn.zonenn (e.g. site12.zone20) as nodename.
SELECT distinct(REGEXP_SUBSTR(B.NODE_NAME,'*site*.*')) as nodename
FROM OPC_ACT_MESSAGES A,OPC_NODE_NAMES B
WHERE A.MESSAGE_GROUP = 'Ebts_Status_Alarms'
AND A.SEVERITY <> 2
AND A.NODE_ID = B.NODE_ID;
SELECT 'site'||site_id||'.zone'||zone_id as nodename
FROM aw_active_alarms
GROUP BY site_id,zone_id;
I need to write a query that select all nodenames from one table that do not exist in the other.
Use left join to find it. It is faster than minus,not in,not exists etc.
SELECT a.nodename
FROM (SELECT DISTINCT( regexp_substr(B.node_name, '*site*.*') ) AS nodename
FROM opc_act_messages A,
opc_node_names B
WHERE A.message_group = 'Ebts_Status_Alarms'
AND A.severity <> 2
AND A.node_id = B.node_id
) a
LEFT JOIN
(SELECT 'site'
|| site_id
|| '.zone'
|| zone_id AS nodename
FROM aw_active_alarms
GROUP BY site_id,
zone_id
) b
ON a.nodename = b.nodename
WHERE b.nodename IS NULL
One simple way: use MINUS
SELECT distinct(REGEXP_SUBSTR(B.NODE_NAME,'*site*.*')) as nodename
FROM OPC_ACT_MESSAGES A,OPC_NODE_NAMES B
WHERE A.MESSAGE_GROUP = 'Ebts_Status_Alarms'
AND A.SEVERITY <> 2
AND A.NODE_ID = B.NODE_ID
MINUS
SELECT 'site'||site_id||'.zone'||zone_id as nodename
FROM aw_active_alarms
GROUP BY site_id,zone_id;
would this work?
WITH t1
AS (SELECT DISTINCT
(REGEXP_SUBSTR (B.NODE_NAME, '*site*.*')) AS nodename
FROM OPC_ACT_MESSAGES A, OPC_NODE_NAMES B
WHERE A.MESSAGE_GROUP = 'Ebts_Status_Alarms'
AND A.SEVERITY <> 2
AND A.NODE_ID = B.NODE_ID),
t2
AS ( SELECT 'site' || site_id || '.zone' || zone_id AS nodename
FROM aw_active_alarms
GROUP BY site_id, zone_id)
SELECT *
FROM t1
WHERE t1.nodename NOT IN (SELECT nodename FROM t2)

Why does this NOT IN query work as intended, but not this NOT EXISTS query?

Working (NOT IN) retrieves 3 rows:
select DISTINCT d.* from Device d , Company c3
WHERE d.deviceid NOT IN
(
Select d1.deviceid from Device d1, Clone x1
WHERE d1.deviceid = x1.deviceID
AND
(
x1.XPath = 'hi'
OR x1.XPath = 'bye'
)
AND
(
EXISTS ( select * from (SELECT * FROM [dbo].[Split] ('T130SF0W2050', ',')) as s
WHERE x1.Value like '%' + s.items + '%' )
)
)
AND
d.companyid = c3.companyid and c3.companynumber in (SELECT * FROM [dbo].[Split] ('00223200', ','))
Not Working(not exists):
select DISTINCT d.* from Device d , Company c3
WHERE NOT EXISTS
(Select * from Device d1, Clone x1
WHERE d1.deviceid = x1.deviceID
AND
(
x1.XPath = 'hi'
OR x1.XPath = 'bye'
)
AND
(
EXISTS ( select * from (SELECT * FROM [dbo].[Split] ('T130SF0W2050', ',')) as s
WHERE x1.Value like '%' + s.items + '%' )
)
)
AND
d.companyid = c3.companyid and c3.companynumber in (SELECT * FROM [dbo].[Split] ('00223200', ','))
I'm unsure I'm using the exists syntax correct, what should I select from the subquery? I've tried a few different combinations. It won't run if I put WHERE d.deviceid NOT EXISTS
Solution (thanks to Nikola):
add AND d1.deviceid = d.deviceid inside the Exists subquery.
The difference is that the NOT IN query returns devices that match the company and don't match the inner query specification.
For the NOT EXIST query to work as written (where "work as written" refers to returning the same result as the top query), there can't be any devices that exist matching the inner query. If any devices match the inner query at all, the query won't return any results.

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary

MySQL/SQL - When are the results of a sub-query avaliable?

Suppose I have this query
SELECT * FROM (
SELECT * FROM table_a
WHERE id > 10 )
AS a_results LEFT JOIN
(SELECT * from table_b
WHERE id IN
(SElECT id FROM a_results)
ON (a_results.id = b_results.id)
I would get the error "a_results is not a table". Anywhere I could use the re-use the results of the subquery?
Edit: It has been noted that this query doesn't make sense...it doesn't, yes. This is just to illustrate the question which I am asking; the 'real' query actually looks something like this:
SELECT SQL_CALC_FOUND_ROWS * FROM
( SELECT wp_pod_tbl_hotel . *
FROM wp_pod_tbl_hotel, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =12
AND wp_pod_rel.tbl_row_id =1
AND wp_pod.tbl_row_id = wp_pod_tbl_hotel.id
AND wp_pod_rel.pod_id = wp_pod.id
) as
found_hotel LEFT JOIN (
SELECT COUNT(*) as review_count, avg( (
location_rating + staff_performance_rating + condition_rating + room_comfort_rating + food_rating + value_rating
) /6 ) AS average_score, hotelid
FROM (
SELECT r. * , wp_pod_rel.tbl_row_id AS hotelid
FROM wp_pod_tbl_review r, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =11
AND wp_pod_rel.pod_id = wp_pod.id
AND r.id = wp_pod.tbl_row_id
AND wp_pod_rel.tbl_row_id
IN (
SELECT wp_pod_tbl_hotel .id
FROM wp_pod_tbl_hotel, wp_pod_rel, wp_pod
WHERE wp_pod_rel.field_id =12
AND wp_pod_rel.tbl_row_id =1
AND wp_pod.tbl_row_id = wp_pod_tbl_hotel.id
AND wp_pod_rel.pod_id = wp_pod.id
)
) AS hotel_reviews
GROUP BY hotel_reviews.hotelid
ORDER BY average_score DESC
AS sorted_hotel ON (id = sorted_hotel.hotelid)
As you can see, the sub-query which makes up the found_query table is repeated elsewhere downward as another sub-query, so I was hoping to re-use the results
You can not use a sub-query like this.
I'm not sure I understand your query, but wouldn't that be sufficient?
SELECT * FROM table_a a
LEFT JOIN table_b b ON ( b.id = a.id )
WHERE a.id > 10
It would return all rows from table_a where id > 10 and LEFT JOIN rows from table_b where id matches.