Update table column based on CTE result - sql

I have the code shown below, and I want to update my original table to reflect the results of this query. I want each record's Route_type column to update with the corresponding value from the Route_type column in the query based on the code associated with each record. For instance, all records with code=1 should have Route_Type updated to "Other" based on the query.
With Route_Number_CTE (Code,Year_and_Week, Route_Count) As
(
Select
Code, Year_and_Week, Count(Route) AS Route_Count
From
Deliveries
Group by
Code, Year_and_Week
)
select
d.Code,
min(r.Route_Count) As Min_Count,
max(r.Route_Count) As Max_Count,
(case
When max(r.Route_Count) = 1 then 'One'
When max(r.Route_Count) <= 3 AND min(r.Route_Count) > 1 then 'Three or less'
When min(r.Route_Count) > 4 then 'Four or More'
Else 'Other'
End) As Route_Type
From
Deliveries as d
inner join
Route_Number_CTE as r on d.Code = r.Code
Group By
d.Code;
Query results:
Code Min_Count Max_Count Route_Type
----------------------------------------
1 1 4 Other
2 1 2 Three or less
3 3 3 Three or less
Deliveries:
Code Route Route_Type
-------------------------
1 A
1 C
1 D
2 A
2 C
2 B
3 A
3 C
3 D

I think that you could use window functions and an updatable cte. This is simpler, and should be more efficient as it avoids the need for aggregation and joins:
with cte as (
select route_type, max(cnt) over(partition by code) max_cnt
from (
select d.*, count(*) over(partition by code, year_and_week) cnt
from deliveries d
) d
)
update cte
set route_type = case
when max_cnt = 1 then 'One'
when max_cnt <= 3 then 'Three or less'
when max_cnt > 4 then 'Four or more'
end

I figured it out by just creating a second CTE from the second Select statement and using an update query with the result.

Related

How do i select all columns, plus the result of the sum

I have this select:
"Select * from table" that return:
Id
Value
1
1
1
1
2
10
2
10
My goal is create a sum from each Value group by id like this:
Id
Value
Sum
1
1
2
1
1
2
2
10
20
2
10
20
I Have tried ways like:
SELECT Id,Value, (SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY IDRNC ) FROM Table v;
But the is not grouping by id.
Id
Value
Sum
1
1
1
1
1
1
2
10
10
2
10
10
Aggregation aggregates rows, reducing the number of records in the output. In this case you want to apply the result of a computation to each of your records, task carried out by the corresponding window function.
SELECT table.*, SUM(Value) OVER(PARTITION BY Id) AS sum_
FROM table
Check the demo here.
Your attempt looks correct.
Can you try the below query :
It works for me :
SELECT Id, Value,
(SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY ID) as sum
FROM Table v;
You can do it using inner join to join with selection grouped by id :
select t.*, sum
from _table t
inner join (
select id, sum(Value) as sum
from _table
group by id
) as s on s.id = t.id
You can check it here
Your select is ok if you adjust it just a little:
SELECT Id,Value, (SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY IDRNC ) FROM Table v;
GROUP BY IDRNC is a mistake and should be GROUP BY ID
you should give an alias to a sum column ...
subquery selecting the sum does not have to have self table alias to be compared with outer query that has one (this is not a mistake - works either way)
Test:
WITH
a_table (ID, VALUE) AS
(
Select 1, 1 From Dual Union All
Select 1, 1 From Dual Union All
Select 2, 10 From Dual Union All
Select 2, 10 From Dual
)
SELECT ID, VALUE, (SELECT SUM(VALUE) FROM a_table WHERE ID = v.ID GROUP BY ID) "ID_SUM" FROM a_table v;
ID VALUE ID_SUM
---------- ---------- ----------
1 1 2
1 1 2
2 10 20
2 10 20

How to merge two query results joining same date

let's say there's a table have data like below
id
status
date
1
4
2022-05
2
3
2022-06
I want find count of id of each month by their status. Something like this below
date
count(status1) = 4
count(status2) =3
2022-05
1
null
2022-06
null
1
I tried doing
-- select distinct (not working)
select date, status1, status2 from
(select date, count(id) as "status1" from myTable
where status = 4 group by date) as myTable1
join
(select date, count(id) as "status2" from myTable
where status = 3 group by date) as myTable2
on myTable1.date = myTable2.date;
-- group by (not working)
but it does duplicate the data needed.
and I am using SQL Server.
select d.date,
sum
(
case
when d.status=4 then 1
else 0
end
)count_status_4,
sum
(
case
when d.status=5 then 1
else 0
end
)count_status_5
from your_table as d
group by d.date

Find subsequent occurrence of a value in a table

I have a table which looks like shown below
ID SubmittedValue ApprovedValue
1 25.9 0
1 29 29
1 25.9 25.9
1 50 0
1 45 0
1 10 0
1 10 10
Expected result
ID SubsequentlyApproved(CNT) Total_Amt_sub_aprvd
1 2 35.9
We get the above result because 25.9+10 since it is repeated in the subsequent rows.
How to perform VLOOKUP like functionality for this scenario. I tried the subquery but it didn't work.
SELECT a.id,
SUM(CASE WHEN a.ApprovedValue=0 THEN 1 ELSE 0 END) AS SUB_COUNT
FROM myTable a
join (select id, sum( case when SubmittedValue=ApprovedValue then 1 end) as check_value from myTable) b
on b.id=a.id and SUB_COUNT=check_value
but this is not giving me the expected result.
You seem to want to count rows where the values are the same and the first value appears more than once. If so, you can use window functions and aggregation:
select id, count(*), sum(ApprovedValue)
from (select t.*, count(*) over (partition by id, SubmittedValue) as cnt
from t
) t
where cnt > 1 and SubmittedValue = ApprovedValue
group by id
Without window functions using a semi-join
select id, count(*), sum(submittedvalue)
from test t1
where submittedvalue=approvedvalue
and exists (select 1
from test t2
where t1.id=t2.id and t1.submittedvalue=t2.submittedvalue
group by id, submittedvalue
having count(*)>1)
group by id;

SQL get the closest two rows within duplicate rows

I have following table
ID Name Stage
1 A 1
1 B 2
1 C 3
1 A 4
1 N 5
1 B 6
1 J 7
1 C 8
1 D 9
1 E 10
I need output as below with parameters A and N need to select closest rows where difference between stage is smallest
ID Name Stage
1 A 4
1 N 5
I need to select rows where difference between stage is smallest
This query can make use of an index on (name, stage) efficiently:
WITH cte AS (
SELECT TOP 1
a.id AS a_id, a.name AS a_name, a.stage AS a_stage
, n.id AS n_id, n.name AS n_name, n.stage AS n_stage
FROM tbl a
CROSS APPLY (
SELECT TOP 1 *, stage - a.stage AS diff
FROM tbl
WHERE name = 'N'
AND stage >= a.stage
ORDER BY stage
UNION ALL
SELECT TOP 1 *, a.stage - stage AS diff
FROM tbl
WHERE name = 'N'
AND stage < a.stage
ORDER BY stage DESC
) n
WHERE a.name = 'A'
ORDER BY diff
)
SELECT a_id AS id, a_name AS name, a_stage AS stage FROM cte
UNION ALL
SELECT n_id, n_name, n_stage FROM cte;
SQL Server uses CROSS APPLY in place of standard-SQL LATERAL.
In case of ties (equal difference) the winner is arbitrary, unless you add more ORDER BY expressions as tiebreaker.
dbfiddle here
This solution works, if u know the minimum difference is always 1
SELECT *
FROM myTable as a
CROSS JOIN myTable as b
where a.stage-b.stage=1;
a.ID a.Name a.Stage b.ID b.Name b.Stage
1 A 4 1 N 5
Or simpler if u don't know the minimum
SELECT *
FROM myTable as a
CROSS JOIN myTable as b
where a.stage-b.stage in (SELECT min (a.stage-b.stage)
FROM myTable as a
CROSS JOIN myTable as b)

Sorting sub-queries in SQL

I've been trying to a query working in SQL 2012 which I'm almost certain I am over complicating
I have a table which stores an order number, item numbers (multiple per order), status codes (multiple per item) and a timestamp
So basically something like this
Order Item Status
1 1 1
1 1 2
2 1 1
2 1 2
2 1 3
3 1 3
3 2 1
3 2 2
Within my query (using this table as the example), I need see the following 1 entry for each line and item but only showing the highest available status... BUT not if the status is 3
So in this case, I'd want to see
Order Item Status
1 1 2
3 2 2
The issue I had is that the query itself works... but it returns the FIRST status code it finds. Not the highest one. So I end up with
Order Item Status
1 1 1
3 2 1
Here's the full expanded code snippet
with summary as (
select a.order_no as order_no, a.item_no as item_no, a.timestamp as timestamp,
max(a.status_code) as status_code, row_number() over (partition by order_no
order by item_no asc) as rn
from db.ordhist a
where a.order_no > 120400000 and a.order_no < 120800000
and a.timestamp < Dateadd(DD,-3,GETDATE() )
and a.status_code >= 133
and not exists (
select b.order_no, b.item_no
from db.ordhist b
where b.status_code in (137,170,201,999)
and b.order_no = a.order_no
and b.item_no = a.item_no)
and not exists (
select c.order_no
from db.ordhist c
where c.status_code = 6
and c.order_no = a.order_no)
group by a.order_no, a.item_no, a.timestamp)
select * from summary where rn = 1
I think you don't need ROW_NUMBER just use a GROUP BY with HAVING MAX([Status])<>3:
SELECT [Order],[Item],MAX([Status])
FROM Table_Name
GROUP BY [Order],[Item]
HAVING MAX([Status])<>3
Ok I think I may have answered my own question.... by removing the grouping within the "summary" and doing grouping in the final results query instead
-- Produced (or higher) but not Delivery Noted --
with summary as (
select a.order_no as order_no, a.item_no as item_no, a.timestamp as timestamp, a.status_code as status_code
from db.ordhist a
where a.order_no > 120400000 and a.order_no < 120800000
and a.timestamp < Dateadd(DD,-3,GETDATE() )
and a.status_code >= 133
and not exists (
select b.order_no, b.item_no
from db.ordhist b
where b.status_code in (137,170,201,999)
and b.order_no = a.order_no
and b.item_no = a.item_no)
and not exists (
select c.order_no
from db.ordhist c
where c.status_code = 6
and c.order_no = a.order_no))
select order_no, item_no, timestamp, max(status_code) from summary
group by order_no, item_no, timestamp
order by status_code