Pivot or unpivot a table - sql-server

I have a table in SQL that I need to pivot or unpivot (not sure which). The table consists of days (as represented by workdays##1, workdays##2, workdays##3,etc.) The values under these days are a business day value for the month. Other columns in this table are Company, Year, Monthnum which should remain as columns. So it looks something like this:
SQL table
Company Year Monthnum workday##1 workday##2 workday##3 workday##4
US 2016 8 1 2 3 4
I need the workday##'s (renamed as day of month and substringed to just the number portion) to pivot to rows and the values under them to be put in one column with the header businessday. Future
Company Year Monthnum Dayofmonth Businessday
US 2016 8 1 (workday##1) 1
US 2016 8 2 (workday##2) 2
US 2016 8 3 (workday##3) 3
US 2016 8 4 (workday##4) 4
This table represents the year and month and the number of business days per month. From this I will be doing some month to date calculations against sales goals on a daily basis.

Related

Linear Interpolation in MS SQL Server

I work with annual mileage for each customer. Years are consecutive in range 2009-2022, so i have no gaps in years and some customers for instance can have records from 2009 to 2020, and so on, but all years are consecutive there are no gaps. The issue with Annual_Mlg is that there are NULL values time to time. NULLs appear randomly: there can be 3 consecutive NULLs at the beginning of range for certain customer or in the middle there can be 4 NULLs, and last 3 values can be NULLs as well. So NULL values are random. I need to have mileage for EVERY SINGLE YEAR. If mileage was 0 that is fine. I just should use it in computation. 0 mileage means customer was not traveling that year at all. So that number is acceptable just like any other valid mileage. By valid mileage i mean non-negative mileage, and table with which i work has ONLY non-negative mileage (so valid mileage). I need to get rid of NULLs and where i have NULLs i use either previous record or next record or interpolation should be used to get rid of NULLs completely. There should be NO default value. I DO HOPE THAT THERE IS A CODE BETTER TO FIX MY PROBLEM. ALSO IF THERE AN EASIER WAY TO FIX PROBLEM IN 'R' (programming language) then I am ALL EARS, however i have not attempted to solve it in 'R' since i suck more in 'R' then in MS SQL.
I have created Previous Mlg and Next Mlg in order to do interpolation if NULL value is sandwiched between non-NULL 2 values. That seems to be working. Annual_mlg this field where the original values are in. Final_mlg this is where i need to have all values as non-NULLs. I used COALESCE to get rid of NULLs to some extent. I am a relatively new in SQL and i have not enough coding skills to tackle this type of problem on my own it seems to be. So if to take a look at what i have inside COALESCE i can explain my logic.
3rd value inside COALESCE deals with case when there are 2 consecutive NULLs somewhere in the middle of 2009-2022 range and then code comes to first NULL it takes previous mileage that is Prev_Mlg, but then for 2nd NULL it takes previous Mileage which i just got and averages it with next non-NULL value.
I use bunch of lead() and lag() those are to account for cases when there are bunch of records with NULLs at either beginning or at the end. This code does not obviously solve all problems and looks like a joke.
I am looking to have more meaningful code. NOTE: there should not be a default value, all NULLs should be replaced and the logic for this goes as follows:
If there is a NULL sandwiched between 2 non-NULLs then code should interpolate;
if there is NULL at the beginning of range then it should be replaced with FIRST non-NULL value in range
by range i mean years from 2009 - 2022
if there is NULL at the end of range then it should be replaced by FIRST non-NULL value (if to count from the end of the range)
Once NULL value is replaced then this should be used in interpolation. I will provide examples what kind of result i need to get.
Any help is GREATLY appreciated!!! THANK YOU SO MUCH!!! I AM REALLY STRUGGLING!!!
SELECT
Customer,
Year,
Final_Mlg = COALESCE(Annual_Mlg, (Prev_Mlg + Next_Mlg)/2,
(lag(Final_Mlg) over (partition by Customer order by Year) + Next_Mlg)/2, Prev_Mlg, Next_Mlg,
lead(Final_Mlg) over (partition by Customer order by Year),
lead(Final_Mlg,2) over (partition by Customer order by Year),
lead(Final_Mlg,3) over (partition by Customer order by Year),
lead(Final_Mlg,4) over (partition by Customer order by Year),
lead(Final_Mlg,5) over (partition by Customer order by Year),
lead(Final_Mlg,6) over (partition by Customer order by Year),
lead(Final_Mlg,7) over (partition by Customer order by Year),
lead(Final_Mlg,8) over (partition by Customer order by Year),
lead(Final_Mlg,9) over (partition by Customer order by Year),
lead(Final_Mlg,10) over (partition by Customer order by Year),
lead(Final_Mlg,11) over (partition by Customer order by Year),
lag(Final_Mlg) over (partition by Customer order by Year),
lag(Final_Mlg,2) over (partition by Customer order by Year),
lag(Final_Mlg,3) over (partition by Customer order by Year),
lag(Final_Mlg,4) over (partition by Customer order by Year),
lag(Final_Mlg,5) over (partition by Customer order by Year),
lag(Final_Mlg,6) over (partition by Customer order by Year),
lag(Final_Mlg,7) over (partition by Customer order by Year),
lag(Final_Mlg,8) over (partition by Customer order by Year),
lag(Final_Mlg,9) over (partition by Customer order by Year),
lag(Final_Mlg,10) over (partition by Customer order by Year),
lag(Final_Mlg,11) over (partition by Customer order by Year)),
Annual_Mlg,
Prev_Mlg,
Next_Mlg
FROM #table2
ORDER BY
Customer,
Year
CASE 1: couple of values are consecutive NULLs at the BEGINNING of range. There is a desired result below.
Year
Customer
Annual_Mileage
2009
A
NULL(Should be Replaced by 3)
2010
A
NULL(Should be Replaced by 3)
2011
A
NULL(Should be Replaced by 3)
2012
A
3
2013
A
4
2014
A
5
2015
A
6
2016
A
7
2017
A
8
2018
A
9
2019
A
10
2020
A
11
2021
A
12
2022
A
13
CASE 2: couple of values are consecutive NULLs at the END of range.
Year
Customer
Annual_Mileage
2009
A
3
2010
A
3
2011
A
3
2012
A
3
2013
A
4
2014
A
5
2015
A
6
2016
A
7
2017
A
8
2018
A
9
2019
A
10
2020
A
NULL(Should be Replaced by 10)
2021
A
NULL(Should be Replaced by 10)
2022
A
NULL(Should be Replaced by 10)
CASE 3: There are some NULLs in between non-NULL values.
Year
Customer
Annual_Mileage
2009
A
1
2010
A
NULL (Should be replaced by 1.5)
2011
A
2
2012
A
3
2013
A
4
2014
A
NULL (Should be replaced by 4.5)
2015
A
5
2016
A
NULL (Should be replaced by 5.5)
2017
A
6
2018
A
NULL (Should be replaced by 6)
2019
A
NULL (Should be replaced by 6.5)
2020
A
7
2021
A
8
2022
A
8

SQL Query-Multiple date ranges

I want to fetch some data from DB by giving multiple date ranges. Example,in February I want to get weekly report from a table in this order Feb 01 to 07, Feb 07 to 14, Feb 14 to 21, Feb 21 to 28 and Feb 28 to Mar 01. In DB the records are stored in a daily wise not in weekly wise. I want to cluster it as weekly wise and calculate sum then show the result. Please help me if you know this case.
For clear cut view, consider 3 tables & its columns.
Table A:id,timestamp (comment-data is inserted daily)
Table B:id,fruits
Table C:id,fruits_type
Result:
fruits_type count(id) timestamp
apple 3 01-02-2016 to 07-02-2016
orange 5 01-02-2016 to 07-02-2016
pineapple 8 01-02-2016 to 07-02-2016
apple 4 07-02-2016 to 14-02-2016
orange 5 07-02-2016 to 14-02-2016
Conditions:id should match among 3 tables;fetch data by providing group by fruits_type and timestamp should be in weekly wise.
Please help if you know this
To get the sum of all values between two dates you would do it like this:
SELECT SUM(Column1)
FROM Table1
WHERE Date1 BETWEEN '2/1/2016' AND Date1 <'2/7/2016'
If you want to make it more flexible and have the query get the last week's sum you can use the DATEADD function to lag by one week:
SELECT SUM(Column1)
FROM Table1
WHERE Date1 BETWEEN DATEADD(week, -1, GETDATE()) AND Date1 < GETDATE()
If you want the result set to include a row for each week, you can use UNION to merge the queries.

Calculate payments on a month by month bases from date opened SQL 2008

I'm currently trying to replicate an old report that used to produce a rolling sum of collections. However it wasn't a standard month on month. Here is screen shot of the excel based report.
The blue section is based on a simple query and gives the dataset used to start(EXAMPLE):
SELECT COUNT(AccountNo) AS Number, SUM(Balance) AS Value, DATENAME(MM,DateOpened) AS Month, DATEPART(Y,DateOpened) AS Year FROM tblAccounts
GROUP BY DATENAME(MM,DateOpened), DATEPART(Y,DateOpened)
The tables are very basic :
AccountNo | Balance | DateOpened
12345 | 1245.55 | 01/01/2015
I'm struggling to get it to work out the months on a rolling basis, so Month 1 for Apr 2011 will be the first month for those files (payments in April), month 2 would be payments in May for the accounts opened in April (I hope that is clear).
So this means Month 1 for April would be April, and Month 1 for Nov would be Nov. Payments are stored in a table tblPayments
AccountNo | DatePayment | PaymentValue
12345 | 02/02/2015 | 15.99
Please ask if I haven't been clear enough
Assuming you have a column called "DatePayment", you should simply do something like this:
SELECT COUNT(AccountNo) AS Number, SUM(Balance) AS Value,
DATENAME(MM,DateOpened) AS Month, DATEPART(Y,DateOpened) AS Year,
DATEDIFF(MONTH, DateOpened, DatePayment) AS MonthN
FROM [...]
GROUP BY DATENAME(MM,DateOpened), DATEPART(Y,DateOpened),
DATEDIFF(MONTH, DateOpened, DatePayment)
The DATEDIFF simply counts the months between the date the account was opened and the date of the payment. Note that you might want to change the DateOpened to always be the 1st of the month in the DATEDIFF calculation.
In the FROM [...] part of your query, you will need a join between your Payments-table and the table holding your accounts, in order to be able to compare DateOpened with DatePayment. You should join them on the AccountNo-column. This looks something like this:
FROM Accounts INNER JOIN Payments ON Accounts.AccountNo = Payments.AccountNo
After doing this, you will need to make sure that all references to columns that exist in both tables are fully qualified. This means that COUNT(AccountNo) should be changed to COUNT(Accounts.AccountNo), etc.

Dividing a value by number of days in a month in a date field in SQL table

I am working on an SQL query which performs some arithmetic division on the data in a SQL Table based on the value in a date column.
To elaborate I have the following SQL table Deals
id Date Type Quantity Price
3 2014-11-04 Sweet 2500 23
9 2014-12-04 Sweet 5000 30
10 2014-12-04 Midale 2500 25.4
11 2014-11-04 Sweet 5000 45
Now, I want to some arithmetic operations on Quantity column grouped by Type and Date.
I used the following query
SELECT
Type,
COUNT(Id) AS Trades,
SUM(Quantity ) AS M3,
ROUND((6.2898*SUM(Quantity ))/31,4) AS BBLperDay,
CAST(Date as date) AS TradeMonth,
ROUND(SUM(Quantity*Price)/Sum(Quantity),4) AS WeightedAverage
FROM Deals
GROUP BY
Type,CAST(Date as date)
The above query returns
Type Trades M3 BBLperDay TradeMonth
Sweet 2 7500 1521.7258 2014-11-04
Midale 1 2500 507.2419 2014-12-04
Sweet 1 5000 1014.4839 2014-12-04
The above results are correct but I hard coded number of days as 31 in the Date in the expression
6.2898*SUM(Quantity )/31 AS BBLperDay
Is there a way I can do just have a value taken from Date column based on the month. If the month is December in the Date column the value will automatically be 31 as number of days in december are 31.
To find no. of days in a month
Select DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,getdate()),0)))
or If you are using SQL SERVER 2012+
Select DAY(EOMONTH(getdate()))
Change your query like this.
SELECT
Type,
COUNT(Id) AS Trades,
SUM(Quantity ) AS M3,
ROUND((6.2898*SUM(Quantity ))/DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,TradeMonth),0))),4) AS BBLperDay,
CAST(Date as date) AS TradeMonth,
ROUND(SUM(Quantity*Price)/Sum(Quantity),4) AS WeightedAverage
FROM Deals
GROUP BY
Type,CAST(Date as date),TradeMonth

One to many Relationship in SQL Server Analysis Services

I have these tables:
DimDate (PK: DateKey, other attributes)
FactActivationCodes (PK: ActivationCode, IssuedDateKey (FK to DimDate)
FactExpirations (PK: ActivationCode + ExpirationType, FK: ActivationCode to FactActivationCodes)
I set up measures that count the number of rows in
Issued Count (count of rows in FactActivationCodes)
Expired Count (count of distinct ActivationCodes in FactExpirations)
The idea is that the FactActivationCodes has one activation code, with a date when it was issued. The activation code can get expired year after year (and then renewed) so it would have a row for expiration in FactExpirations (one each year)
I put some test rows in the tables; I put 3 rows in FactActivationCodes (different IssuedDate for each) , and only 2 in FactExpirations. When I browse the cube, and I am looking at the count of Issued on columns, and the Issued Date (dimension) on rows, it looks like this:
Issued Date
January 2008 1
February 2008 1
March 2008 1
But then, when I add the Expired Count, I was hoping to see the 'expired column' count with only the ones that match the 'Activation Code' like so, because of the one to many relationship between the two fact tables:
Issued Date Expired Date
January 2008 1 1
February 2008 1 1
March 2008 1 0
But instead, I a cross join of everything like so, with the totals of expired:
Issued Date Expired Date
January 2008 1 2
February 2008 1 2
March 2008 1 2
April 2008 2
May 2008 2
June 2008 2
And onwards, for every date entry in my Date Dimensions... I guess I'm not doing the relationship correctly... how can I get the expected result?
The answer to use referenced relationship: http://technet.microsoft.com/en-us/library/ms166704.aspx

Resources