Update table based on another table with date condition - sql

UTILITYREADING TABLE
ROOMNUMBER
ELECTRICITYREADING
DATEOFREADING
N201
279.8
2/15/2022
N201
240.6
1/16/2022
N201
240.6
12/15/2021
N202
299.8
2/15/2022
N202
259.8
1/15/2022
UTILITYINVOICE table should look like this:
ROOMNUMBER
ELECCURRENT
ELECPREVIOUS
INVOICEDATE
N201
279.8
240.6
2/19/2022
N202
299.8
259.8
2/19/2022
I was able to insert the ELECCURRENT value (or the current month February reading) by executing:
INSERT INTO UTILITYINVOICE (ROOMNUMBER, ELECCURRENT, INVOICEDATE)
SELECT
ROOMNUMBER, NVL(ELECTRICITYREADING, 0), SYSDATE
FROM UTILITYREADING
WHERE (extract(MONTH from DATEOFREADING) = extract(MONTH from SYSDATE))
AND (extract(YEAR from DATEOFREADING) = extract(YEAR from SYSDATE));
My problem is fetching the February reading, to fetch the January reading i tried this but with no success:
UPDATE UTILITYINVOICE
SET ELECPREVIOUS = (SELECT NVL(ELECTRICITYREADING, 0)
FROM UTILITYREADING)
WHERE EXISTS (SELECT 1 FROM utilityreading
WHERE (extract(MONTH from UTILITYREADING.DATEOFREADING) = extract(MONTH from SYSDATE)-1)
AND (extract(YEAR from UTILITYREADING.DATEOFREADING) = extract(YEAR from SYSDATE))
AND utilityreading.roomnumber = utilityinvoice.roomnumber);

I'd say that you need
LAG analytic function (to select previous electricity reading)
ROW_NUMBER (to sort rows per each room number by date in descending order)
and then fetch row that ranks as the highest
SQL> insert into utilityinvoice (roomnumber, eleccurrent, elecprevious, invoicedate)
2 with temp as
3 (select roomnumber,
4 electricityreading as eleccurrent,
5 lag(electricityreading) over (partition by roomnumber order by dateofreading) elecprevious,
6 row_number() Over (partition by roomnumber order by dateofreading desc) rn
7 from utilityreading
8 )
9 select roomnumber, eleccurrent, elecprevious, sysdate
10 from temp
11 where rn = 1;
2 rows created.
SQL> select * from utilityinvoice;
ROOM ELECCURRENT ELECPREVIOUS INVOICEDAT
---- ----------- ------------ ----------
N201 279,8 240,6 02/19/2022
N202 299,8 259,8 02/19/2022
SQL>

Related

SQL query to pick up the previous row and the current row with the last update date condition

I have a table ANC_PER_ABS_ENTRIES that has the following detail -
PER_AB_ENTRY_ID
person_number
action_type
duration
START_dATE
END_DATE
LAST_UPD_DT
15
101
INSERT
3
01/10/2022
03/10/2022
2022-11-01T04:59:43
15
101
UPDATE
1
01/10/2022
01/10/2022
2022-11-02T10:59:43
This table is a history table and the action like - insert update and delete are tracked in this table.
Insert means when the entry in the table was added
Update means some sort of changes were made in the table
Delete means the record was deleted.
I want to create a query that picks up the changes in this table by comparing the last_update_date and the run_date (parameter)
Eg- for person_number 101 with per_ab_entry_id -- > 15 , the action_type is insert first that means the record was created on first, then it is updated and the end_date , duration is changed.
so if i run the below query on the 1st after 4:59, then the 1st row will be picked.
When I run it on 2nd , only the 2nd row is getting picked.
But how i want is that in case sthe same per_ab_entry_id was updated and if the last_upd_dt of the update >= run_Date then , the insert row should also be extracted -
The output should look like this in the latest run-
PER_AB_ENTRY_ID
person_number
flag
duration
START_dATE
END_DATE
LAST_UPD_DT
15
101
O
3
01/10/2022
03/10/2022
2022-11-01T04:59:43
15
101
u
1
01/10/2022
01/10/2022
2022-11-02T10:59:43
I have to run the below query such that the last_update_date >= :process_date.
Its working for the delete condition and evrything except this case. How can it be tweaked that when the last_upd_dt of the latest recorrd of one per_ab_entry_id >=process_date then its previous row is also sent.
The below query is not working because the last_upd_dt of the 1st row <= process_date
with anc as
(
select person_number,
absence_type,
ABSENCE_STATUS,
approval_status_cd,
start_date,
end_date,
duration,
PER_AB_ENTRY_ID,
AUDIT_ACTION_TYPE_,
row_number() over (order by PER_AB_ENTRY_ID, LAST_UPD_DT) rn
from ANC_PER_ABS_ENTRIES
)
SELECT * FROM ANC
where RN = 1
or RN = 2 and UPPER(flag) = 'D'
and APPROVAL_STATUS_CD = 'Approved'
and last_update_date >=:process_date
ORder by PER_AB_ENTRY_ID, LAST_UPD_DT
I understand that you want to find entries that were updated recently, and display their whole change log (starting with the initial insert).
Here is one way to do it with window functions:
select a.*
from (
select a.*,
max(last_update_date) over(partition by per_ab_entry_id) max_update_date
from anc_per_abs_entries a
) a
where max_update_date >= :process_date
order by per_ab_entry_id, last_update_date
In the subquery, the window max computes the latest update amongst all rows that belong to the same entry ; we can then use that information to filter in the outer query.
I was not sure about the filtering logic at the end of your SQL code, which is not described in the text of the question, so I left it apart - you might need to reincorporate it.
Try to create CTE from your table like this:
WITH
anc AS
(
Select
ROW_NUMBER() OVER(Partition By PER_AB_ENTRY_ID, PERSON_NUMBER Order By PER_AB_ENTRY_ID, PERSON_NUMBER, LAST_UPDATE_DATE) "RN",
Count(*) OVER(Partition By PER_AB_ENTRY_ID, PERSON_NUMBER Order By PER_AB_ENTRY_ID, PERSON_NUMBER) "MAX_RN",
a.*, Max(LAST_UPDATE_DATE) OVER(Partition By PER_AB_ENTRY_ID, PERSON_NUMBER Order By PER_AB_ENTRY_ID, PERSON_NUMBER, LAST_UPDATE_DATE) "MAX_UPD_DATE"
From anc_per_abs_entries a
)
Now you have last (max) update date along with row numbers and total number of rows per person and entry id.
The main SQL should do the job:
SELECT
a.RN,
a.MAX_RN,
a.PER_AB_ENTRY_ID,
a.PERSON_NUMBER,
a.ACTION_TYPE,
a.DURATION,
a.START_DATE,
a.END_DATE,
a.LAST_UPDATE_DATE,
To_Char(a.MAX_UPD_DATE, 'dd.mm.yyyy hh24:mi:ss') "MAX_UPD_DATE"
FROM
anc a
WHERE
LAST_UPDATE_DATE <= :process_date
AND
(
(
TRUNC(a.MAX_UPD_DATE) = TRUNC(:process_date) And
a.MAX_UPD_DATE <= :process_date And
a.RN = a.MAX_RN
)
OR
(
TRUNC(a.MAX_UPD_DATE) = TRUNC(:process_date) And
a.MAX_UPD_DATE > :process_date And
a.RN = a.MAX_RN - 1
)
OR
(
TRUNC(a.MAX_UPD_DATE) != TRUNC(:process_date) And
a.MAX_UPD_DATE > :process_date And
a.RN IN(a.MAX_RN, a.MAX_RN - 1)
)
)
Now you can adjust every part of the OR conditions to suit your needs. For instance I'm not sure what you want to select if second group of conditions is satisfied. It is either that in the code or maybe like here:
...
OR
(
TRUNC(a.MAX_UPD_DATE) = TRUNC(:process_date) And
a.MAX_UPD_DATE > :process_date And
a.RN = CASE WHEN a.MAX_RN > 1 THEN a.MAX_RN - 1 ELSE a.MAX_RN END
)
...
... or whatever suits you.
Regards...

SQL Divide previous row balance by current row balance and insert that value into current rows column "Growth"

I have a table where like this.
Year
ProcessDate
Month
Balance
RowNum
Calculation
2022
20220430
4
22855547
1
2022
20220330
3
22644455
2
2022
20220230
2
22588666
3
2022
20220130
1
33545444
4
2022
20221230
12
22466666
5
I need to take the previous row of each column and divide that amount by the current row.
Ex: Row 1 calculation should = Row 2 Balance / Row 1 Balance (22644455/22855547 = .99% )
Row 2 calculation should = Row 3 Balance / Row 2 Balance etc....
Table is just a Temporary table I created titled #MonthlyLoanBalance2.
Now I just need to take it a step further.
Let me know what and how you would go about doing this.
Thank you in advance!
Insert into #MonthlytLoanBalance2 (
Year
,ProcessDate
,Month
,Balance
,RowNum
)
select
--CloseYearMonth,
left(ProcessDate,4) as 'Year',
ProcessDate,
--x.LOANTypeKey,
SUBSTRING(CAST(x.ProcessDate as varchar(38)),5,2) as 'Month',
sum(x.currentBalance) as Balance
,ROW_NUMBER()over (order by ProcessDate desc) as RowNum
from
(
select
distinct LoanServiceKey,
LoanTypeKey,
AccountNumber,
CurrentBalance,
OpenDateKey,
CloseDateKey,
ProcessDate
from
cu.LAFactLoanSnapShot
where LoanStatus = 'Open'
and LoanTypeKey = 0
and ProcessDate in (select DateKey from dimDate
where IsLastDayOfMonth = 'Y'
and DateKey > convert(varchar, getdate()-4000, 112)
)
) x
group by ProcessDate
order by ProcessDate desc;``
I am assuming your data is already prepared as shown in the table. Now you can try Lead() function to resolve your issue. Remember format() function is used for taking only two precision.
SELECT *,
FORMAT((ISNULL(LEAD(Balance,1) OVER (ORDER BY RowNum), 1)/Balance),'N2') Calculation
FROM #MonthlytLoanBalance2

SQL - Calculate the difference in number of orders by month

I am working on the orders table provided by this site, it has its own editor where you can test your SQL statements.
The order table looks like this
order_id
customer_id
order_date
1
7000
2016/04/18
2
5000
2016/04/18
3
8000
2016/04/19
4
4000
2016/04/20
5
NULL
2016/05/01
I want to get the difference in the number of orders for subsequent months.
To elaborate, the number of orders each month would be like this
SQL Statement
SELECT
MONTH(order_date) AS Month,
COUNT(MONTH(order_date)) AS Total_Orders
FROM
orders
GROUP BY
MONTH(order_date)
Result:
Month
Total_Orders
4
4
5
1
Now my goal is to get the difference in subsequent months which would be
Month
Total_Orders
Total_Orders_Diff
4
4
4 - Null = Null
5
1
1 - 4 = -3
My strategy was to self-join following this answer
This was my attempt
SELECT
MONTH(a.order_date),
COUNT(MONTH(a.order_date)),
COUNT(MONTH(b.order_date)) - COUNT(MONTH(a.order_date)) AS prev,
MONTH(b.order_date)
FROM
orders a
LEFT JOIN
orders b ON MONTH(a.order_date) = MONTH(b.order_date) - 1
GROUP BY
MONTH(a.order_date)
However, the result was just zeros (as shown below) which suggests that I am just subtracting from the same value rather than from the previous month (or subtracting from a null value)
MONTH(a.order_date)
COUNT(MONTH(a.order_date))
prev
MONTH(b.order_date)
4
4
0
NULL
5
1
0
NULL
Do you have any suggestions as to what I am doing wrong?
You have to use LAG window function in your SELECT statement.
LAG provides access to a row at a given physical offset that comes
before the current row.
So, this is what you need:
SELECT
MONTH(order_date) as Month,
COUNT(MONTH(order_date)) as Total_Orders,
COUNT(MONTH(order_date)) - (LAG (COUNT(MONTH(order_date))) OVER (ORDER BY (SELECT NULL))) AS Total_Orders_Diff
FROM orders
GROUP BY MONTH(order_date);
Here in an example on the SQL Fiddle: http://sqlfiddle.com/#!18/5ed75/1
Solution without using LAG window function:
WITH InitCTE AS
(
SELECT MONTH(order_date) AS Month,
COUNT(MONTH(order_date)) AS Total_Orders
FROM orders
GROUP BY MONTH(order_date)
)
SELECT InitCTE.Month, InitCTE.Total_Orders, R.Total_Orders_Diff
FROM InitCTE
OUTER APPLY (SELECT TOP 1 InitCTE.Total_Orders - CompareCTE.Total_Orders AS Total_Orders_Diff
FROM InitCTE AS CompareCTE
WHERE CompareCTE.Month < InitCTE.Month) R;
Something like the following should give you what you want - disclaimer, untested!
select *, Total_Orders - lag(Total_orders,1) over(order by Month) as Total_Orders_Diff
from (
select Month(order_date) as Month, Count(*) as Total_Orders
From orders
Group by Month(order_date)
)o

Bigquery SQL MAX() text value

Thanks for taking the time to look at this.
How to get the MAX of a text value in column A. I would like to and another where clause to show only one of the "FY20-Q4M#" (MAX value only)
Current Table
YY-QQ_STATUS
Program
FY20-Q2_ACTUALS
XYZ
FY20-Q3_ACTUALS
XYZ
FY20-Q3_BUDGET
XYZ
FY20-Q4M0
XYZ
FY20-Q4M1
XYZ
FY20-Q4M2
XYZ
FY20-Q4_BUDGET
XYZ
FY20-Q4_OUTLOOK
XYZ
Goal:
YY-QQ_STATUS
Program
FY20-Q2_ACTUALS
XYZ
FY20-Q3_ACTUALS
XYZ
FY20-Q3_BUDGET
XYZ
FY20-Q4M2
XYZ
FY20-Q4_BUDGET
XYZ
FY20-Q4_OUTLOOK
XYZ
SELECT
CASE WHEN UPPER(SCENARIO) LIKE '%ACTUALS%' THEN CONCAT(LEFT(FISCAL_YEAR,4) ,'-', QUARTER, '_ACTUALS')
WHEN UPPER(SCENARIO) LIKE 'Q%M%' THEN CONCAT(LEFT(FISCAL_YEAR,4) ,'-', QUARTER, SUBSTR(SCENARIO,3,2))
WHEN UPPER(SCENARIO) LIKE '%BUDGET%' THEN CONCAT(LEFT(FISCAL_YEAR,4) ,'-', QUARTER, '_BUDGET')
END AS SCENARIO,
CONCAT(LEFT(FISCAL_YEAR,4) ,'-', QUARTER) AS QUARTER,
PROGRAM_NAME,
FROM [XXXXFinance.FINANCE_OPS_REPORT_V] FIN
WHERE PROGRAM_NAME = 'Kiev20'
AND (XYZ_PER_NAME_QUARTER >= (SELECT XYZ_PER_NAME_QUARTER FROM [XXXXMaster_Data.CALENDAR]
WHERE CALENDAR_DATE_STR = CURRENT_DATE())
OR XYZ_PER_NAME_QUARTER < (SELECT XYZ_PER_NAME_QUARTER FROM [XXXXMaster_Data.CALENDAR]
WHERE CALENDAR_DATE_STR = CURRENT_DATE()) AND (SCENARIO CONTAINS 'Actual' OR SCENARIO CONTAINS 'Budget')
OR XYZ_PER_NAME_QUARTER < (SELECT XYZ_PER_NAME_QUARTER FROM [XXXXMaster_Data.CALENDAR]
WHERE CALENDAR_DATE_STR = CURRENT_DATE()) AND (SCENARIO CONTAINS 'Q%M%'))
group by 1,2,3
order by SCENARIO```
Try below
select
ifnull(format('FY%s-Q%sM%s', any_value(year), any_value(quartal), max(month)), any_value(YY_QQ_STATUS)) as YY_QQ_STATUS,
Program
from (
select *,
regexp_extract(YY_QQ_STATUS, r'FY(\d\d)-Q\dM\d') year,
regexp_extract(YY_QQ_STATUS, r'FY\d\d-Q(\d)M\d') quartal,
regexp_extract(YY_QQ_STATUS, r'FY\d\d-Q\dM(\d)') month
from `project.XXXXFinance.FINANCE_OPS_REPORT_V`
)
group by Program, ifnull(format('FY%s-Q%s', year, quartal), YY_QQ_STATUS)
if applied to sample data in y our question - output is
If you want one of the values, you could use row_number() and choose one row:
select t.* except (seqnum)
from (select t.*,
row_number() over (partition by left(YY_QQ_STATUS, 7) order by YY_QQ_STATUS desc) as seqnum
from t
) t
where not (YY_QQ_STATUS like 'FY20-Q4%' and seqnum > 1);

How to select the last 12 months in sql?

I need to select the last 12 months. As you can see on the picture, May occurs two times.
But I only want it to occur once. And it needs to be the newest one.
Plus, the table should stay in this structure, with the latest month on the bottom.
And this is the query:
SELECT Monat2,
Monat,
CASE WHEN NPLAY_IND = '4P'
THEN 'QuadruplePlay'
WHEN NPLAY_IND = '3P'
THEN 'TriplePlay'
WHEN NPLAY_IND = '2P'
THEN 'DoublePlay'
WHEN NPLAY_IND = '1P'
THEN 'SinglePlay'
END AS Series,
Anzahl as Cnt
FROM T_Play_n
where NPLAY_IND != '0P'
order by Series asc ,Monat
This is the new query
SELECT sub.Monat2,sub.Monat,
CASE WHEN NPLAY_IND = '4P'
THEN 'QuadruplePlay'
WHEN NPLAY_IND = '3P'
THEN 'TriplePlay'
WHEN NPLAY_IND = '2P'
THEN 'DoublePlay'
WHEN NPLAY_IND = '1P'
THEN 'SinglePlay'
END
AS Series, Anzahl as Cnt FROM (SELECT ROW_NUMBER () OVER (PARTITION BY Monat2 ORDER BY Monat DESC)rn,
Monat2,
Monat,
Anzahl,
NPLAY_IND
FROM T_Play_n)sub
where sub.rn = 1
It does only show the months once but it doesn't do that for every Series.
So with every Play it should have 12 months.
In Oracle and SQL-Server you can use ROW_NUMBER.
name = month name and num = month number:
SELECT sub.name, sub.num
FROM (SELECT ROW_NUMBER () OVER (PARTITION BY name ORDER BY num DESC) rn,
name,
num
FROM tab) sub
WHERE sub.rn = 1
ORDER BY num DESC;
WITH R(N) AS
(
SELECT 0
UNION ALL
SELECT N+1
FROM R
WHERE N < 12
)
SELECT LEFT(DATENAME(MONTH,DATEADD(MONTH,-N,GETDATE())),3) AS [month]
FROM R
The With R(N) is a Common Table Expression.The R is the name of the result set (or table) that you are generating. And the N is the month number.
In SQL Server you can do It in following:
SELECT DateMonth, DateWithMonth -- Specify columns to select
FROM Tbl -- Source table
WHERE CAST(CAST(DateWithMonth AS INT) * 100 + 1 AS VARCHAR(20)) >= DATEADD(MONTH, -12,GETDATE()) -- Condition to return data for last 12 months
GROUP BY DateMonth, DateWithMonth -- Uniqueness
ORDER BY DateWithMonth -- Sorting to get latest records on the bottom
So it sounds like you want to select rows that contain the last occurrence of months. Something like this should work:
select * from [table_name]
where id in (select max(id) from [table_name] group by [month_column])
The last select in the brackets will get a list of id's for the last occurrence of each month. If the year+month column you have shown is not in descending order already, you might want to max this column instead.
You can use something like this(the table dbo.Nums contains int values from 0 to 11)
SELECT DATEADD(MONTH, DATEDIFF(MONTH, '19991201', CURRENT_TIMESTAMP) + n - 12, '19991201'),
DATENAME(MONTH,DateAdd(Month, DATEDIFF(month, '19991201', CURRENT_TIMESTAMP) + n - 12, '19991201'))
FROM dbo.Nums
I suggest to use a group by for the month name, and a max function for the numeric component. If is not numeric, use to_number().