Joining From Differently Formatted Tables - sql

Table 1:
ID Year Month
-----------------
1 2018 1
2 2018 1
3 2018 1
1 2018 2
2 2018 2
3 2018 2
Table 2:
ID Year Jan Feb Mar
------------------------
1 2018 100 200 300
2 2018 200 400 300
3 2018 200 500 700
How can I join these two tables even though they are laid out differently?
I was exploring a case join but that doesn't seem to be exactly what I need.
I'd like my output to be:
ID Year Month Data
1 2018 1 100
2 2018 1 200
3 2018 1 200
1 2018 2 200
2 2018 2 400
3 2018 2 500
1 2018 3 300
2 2018 3 300
3 2018 3 700

So, firstly we get TableB in the right format:
SELECT B.ID, B.Year, B.MonthValue
INTO TableB_New
FROM TableB T
UNPIVOT
(
MonthValue FOR Month IN (Jan, Feb, Mar)
) AS B
And then you do the join. Good Luck!

Related

sql split yearly record into 12 monthly records

I am trying to use common table expression to split an yearly record into 12 monthly records. I have to do it for next 20 years records . That means 20 rows into 600 rows (20*12=600 records).
What is the best way to do it. Can anyone help with an efficient way to do it.
Using a single table as shown below. Year 0 means current year so it should split into remaining months and year=1 means next year onward it should split into 12 (months) records
id year value
1 0 3155174.87
1 1 30423037.3
1 2 35339631.25
expected result should look like this:
Id Year Month Value Calender year
1 0 5 150 2022
1 0 6 150 2022
1 0 7 150 2022
1 0 8 150 2022
1 0 9 150 2022
1 0 10 150 2022
1 0 11 150 2022
1 0 12 150 2022
1 0 1 150 2023
1 0 2 150 2023
1 0 3 150 2023
1 0 4 150 2023
1 1 5 100 2023
1 1 6 100 2023
1 1 7 100 2023
1 1 8 100 2023
1 1 9 100 2023
1 1 10 100 2023
1 1 11 100 2023
1 1 12 100 2023
1 1 1 100 2024
1 1 2 100 2024
1 1 3 100 2024
1 1 4 100 2024
You can simply join onto a list of months, and then use a bit of arithmetic to split the Value
SELECT
t.Id,
t.Year,
v.Month,
Value = t.Value / CASE WHEN t.Year = 0 THEN 13 - MONTH(GETDATE()) ELSE 12 END
FROM YourTable t
JOIN (VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
) v(Month) ON t.year > 0 OR v.Month >= MONTH(GETDATE());
db<>fiddle

Loop Record in Oracle

I have a Table called TaxAmount. It has 3 columns(ID, Year, Amount). refer the below image.
I want to divide each row into 12 months. I attached a sample image below.
I'm new in Oracle side. please help me to write a Oracle Query to display the above result.
I tried ROWNUM. But No luck.
Here's one option:
SQL> select id, year, column_value as month, amount
2 from taxamount cross join
3 table(cast(multiset(select level from dual
4 connect by level <= 12
5 ) as sys.odcinumberlist))
6 order by id, year, month;
ID YEAR MONTH AMOUNT
---------- ---------- ---------- ----------
1 2022 1 100
1 2022 2 100
1 2022 3 100
1 2022 4 100
1 2022 5 100
1 2022 6 100
1 2022 7 100
1 2022 8 100
1 2022 9 100
1 2022 10 100
1 2022 11 100
1 2022 12 100
2 2022 1 200
2 2022 2 200
2 2022 3 200
2 2022 4 200
2 2022 5 200
2 2022 6 200
2 2022 7 200
2 2022 8 200
2 2022 9 200
2 2022 10 200
2 2022 11 200
2 2022 12 200
3 2022 1 150
3 2022 2 150
3 2022 3 150
3 2022 4 150
3 2022 5 150
3 2022 6 150
3 2022 7 150
3 2022 8 150
3 2022 9 150
3 2022 10 150
3 2022 11 150
3 2022 12 150
36 rows selected.
SQL>

Enter missing month and calculate running total in one stored procedure

How can I select data from this table
Yr Month El1 Value
---- ------ ------- ------
2017 2 AT010 100
2017 3 AT010 100
2017 4 AT010 50
2017 5 AT010 150
2017 3 BE020 10
.......
and insert it into another table in the following way
Yr Month El1 Value
---- ------ ------- ------
2017 0 AT010 0
2017 1 AT010 0
2017 2 AT010 100
2017 3 AT010 200
2017 4 AT010 250
2017 5 AT010 400
2017 6 AT010 400
2017 7 AT010 400
2017 8 AT010 400
2017 9 AT010 400
2017 10 AT010 400
2017 11 AT010 400
2017 12 AT010 400
2017 0 BE020 0
2017 1 BE020 0
2017 2 BE020 0
2017 3 BE020 10
2017 4 BE020 10
2017 5 BE020 10
2017 6 BE020 10
2017 7 BE020 10
2017 8 BE020 10
2017 9 BE020 10
2017 10 BE020 10
2017 11 BE020 10
2017 12 BE020 10
.......
I am trying to insert missing months from 0 to 12 and calculate running total at the same time.
I used this suggestion for running total calculation; however, I can't figure out how to enter missing month.
This code will be used in the stored procedure for a daily ETL job.
Hmmm . . . Generate the rows and then use left join or outer apply to bring in the values. Here is one way:
with yyyymm as (
select 2017 as yr, 1 as mom
union all
select yr, mon + 1
from yyyymm
where mon + 1 <= 12
)
select yyyymm.yr, yyyymm.mon, coalesce(e.el1, 0) as el1
from yyyymm cross join
(select distinct el1 from t) e outer apply
(select sum(t.value)
from t
where t.el1 = e.el1 and
t.yr = yyyy.yr and
t.month <= yyyy.mon
) tt
order by e.el1, yyyy.yr, yyyy.month;

SQL: Populating missing months and values

I have a table that looks like the following:
Accountno | MonthPeriod | YearPeriod | Value | StartYear | StartMonth | EndYear | Endmonth
---------- ------------- ------------ ------- ----------- ------------ --------- --------
210 4 2015 100 2015 1 2016 5
210 2 2016 200 2015 1 2016 5
300 4 2015 50 2015 1 2016 5
300 2 2016 100 2015 1 2016 5
I am looking for a way to fill in the missing months and years between the startmonth/startyear and endmonth/endyear, where the values for the newly populated entries will be of the previously populated values - populating the values backwards if that makes any sense? And for the entries towards the endyear/endmonth the accountno's last value (As the desired output table below)
Accountno | MonthPeriod | YearPeriod | Value | StartYear | StartMonth | EndYear | Endmonth
---------- ------------- ------------ ------- ----------- ------------ --------- --------
210 1 2015 100 2015 1 2016 5
210 2 2015 100 2015 1 2016 5
210 3 2015 100 2015 1 2016 5
210 4 2015 200 2015 1 2016 5
210 5 2015 200 2015 1 2016 5
210 6 2015 200 2015 1 2016 5
210 7 2015 200 2015 1 2016 5
210 8 2015 200 2015 1 2016 5
210 9 2015 200 2015 1 2016 5
210 10 2015 200 2015 1 2016 5
210 11 2015 200 2015 1 2016 5
210 12 2015 200 2015 1 2016 5
210 1 2016 200 2015 1 2016 5
210 2 2016 200 2015 1 2016 5
210 3 2016 200 2015 1 2016 5
210 4 2016 200 2015 1 2016 5
210 5 2016 200 2015 1 2016 5
300 1 2015 50 2015 1 2016 5
300 2 2015 50 2015 1 2016 5
300 3 2015 50 2015 1 2016 5
300 4 2015 50 2015 1 2016 5
300 5 2015 100 2015 1 2016 5
300 6 2015 100 2015 1 2016 5
300 7 2015 100 2015 1 2016 5
300 8 2015 100 2015 1 2016 5
300 9 2015 100 2015 1 2016 5
300 10 2015 100 2015 1 2016 5
300 11 2015 100 2015 1 2016 5
300 12 2015 100 2015 1 2016 5
300 1 2016 100 2015 1 2016 5
300 2 2016 100 2015 1 2016 5
300 3 2016 100 2015 1 2016 5
300 4 2016 100 2015 1 2016 5
300 5 2016 100 2015 1 2016 5
I tried manipulating the following code for my situation but did not exceed:
CREATE TABLE TEST( Month tinyint, Year int, Value int)
INSERT INTO TEST(Month, Year, Value)
VALUES
(1,2013,100),
(4,2013,101),
(8,2013,102),
(2,2014,103),
(4,2014,104)
DECLARE #Months Table(Month tinyint)
Insert into #Months(Month)Values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10), (11),(12);
With tblValues as (
select Rank() Over (ORDER BY y.Year, m.Month) as [Rank],
m.Month,
y.Year,
t.Value
from #Months m
CROSS JOIN ( Select Distinct Year from Test ) y
LEFT JOIN Test t on t.Month = m.Month and t.Year = y.Year
)
Select t.Month, t.Year, COALESCE(t.Value, t1.Value) as Value
from tblValues t
left join tblValues t1 on t1.Rank = (
Select Max(tmax.Rank)
From tblValues tmax
Where tmax.Rank < t.Rank AND tmax.Value is not null)
Order by t.Year, t.Month
Any assistance will be greatly appreciated!

return the last row that meets a condition in sql

I have two tables:
Meter
ID SerialNumber
=======================
1 ABC1
2 ABC2
3 ABC3
4 ABC4
5 ABC5
6 ABC6
RegisterLevelInformation
ID MeterID ReadValue Consumption PreviousReadDate ReadType
============================================================================
1 1 250 250 1 jan 2015 EST
2 1 550 300 1 feb 2015 ACT
3 1 1000 450 1 apr 2015 EST
4 2 350 350 1 jan 2015 EST
5 2 850 500 1 feb 2015 ACT
6 2 1000 150 1 apr 2015 ACT
7 3 1500 1500 1 jan 2015 EST
8 3 2500 1000 1 mar 2015 EST
9 3 5000 2500 4 apr 2015 EST
10 4 250 250 1 jan 2015 EST
11 4 550 300 1 feb 2015 ACT
12 4 1000 450 1 apr 2015 EST
13 5 350 350 1 jan 2015 ACT
14 5 850 500 1 feb 2015 ACT
15 5 1000 150 1 apr 2015 ACT
16 6 1500 1500 1 jan 2015 EST
17 6 2500 1000 1 mar 2015 EST
18 6 5000 2500 4 apr 2015 EST
I am trying to group by meter serial and return the last actual read date for each of the meters but I am unsure as to how to accomplish this. Here is the sql I have thus far:
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode, PreviousReadDate
order by a.SerialNumber
I can't seem to get the MAX function to take effect in returning only the latest actual reading row and it returns all dates and the same meter serial is displayed several times.
If I use the following sql:
select a.SerialNumber, count(*) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
group by a.SerialNumber
order by a.SerialNumber
then each serial is shown only once. Any help would be greatly appreciated.
Like #PaulGriffin said in his comment you need to remove PreviousReadDate column from your GROUP BY clause.
Why are you experiencing this behaviour?
Basically the partition you have chosen - (SerialNumber,ReadTypeCode,PreviousReadDate) for each distinct pair of those values prints you SerialNumber, ReadTypeCode, MAX(PreviousReadDate). Since you are applying a MAX() function to each row of the partition that includes this column you are simply using an aggregate function on one value - so the output of MAX() will be equal to the one without it.
What you wanted to achieve
Get MAX value of PreviousReadDate for every pair of (SerialNumber,ReadTypeCode). So this is what your GROUP BY clause should include.
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode
order by a.SerialNumber
Is the correct SQL query for what you want.
Difference example
ID MeterID ReadValue Consumption PreviousReadDate ReadType
============================================================================
1 1 250 250 1 jan 2015 EST
2 1 550 300 1 feb 2015 ACT
3 1 1000 450 1 apr 2015 EST
Here if you apply the query with grouping by 3 columns you would get result:
SerialNumber | ReadTypeCode | PreviousReadDate
ABC1 | EST | 1 jan 2015 -- which is MAX of 1 value (1 jan 2015)
ABC1 | ACT | 1 feb 2015
ABC1 | EST | 1 apr 2015
But instead when you only group by SerialNumber,ReadTypeCode it would yield result (considering the sample data that I posted):
SerialNumber | ReadTypeCode | PreviousReadDate
ABC1 | EST | 1 apr 2015 -- which is MAX of 2 values (1 jan 2015, 1 apr 2015)
ABC1 | ACT | 1 feb 2015 -- which is MAX of 1 value (because ReadTypeCode is different from the row above
Explanation of your second query
In this query - you are right indeed - each serial is shown only once.
select a.SerialNumber, count(*) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
group by a.SerialNumber
order by a.SerialNumber
But this query would produce you odd results you don't expect if you add grouping by more columns (which you have done in your first query - try it yourself).
You need to remove PreviousReadDate from your Group By clause.
This is what your query should look like:
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode
order by a.SerialNumber
To understand how the group by clause works when you mention multiple columns, follow this link: Using group by on multiple columns
You will understand what was wrong with your query and why it returns all dates and the same meter serial is displayed several times.
Good luck!
Kudos! :)