simple unpivot on 2 rows table - sql-server-2008

I have table that contains data of query, sample data looks like this:
| YEAR | JAN | FEB | ... | DEC |
|------|-----|-----|-----|-----|
| 2013 | 90 | 40 | ... | 50 |
| 2014 | 30 | 20 | ... | 40 |
I'm trying to unpivot this table to have data like this:
| MONTH | 2013 | 2014 |
|-------|------|------|
| JAN | 90 | 30 |
| FEB | 40 | 20 |
| ... | ... | ... |
| DEC | 50 | 40 |
I've tried this:
select Month, 2013, 2014
from Data
unpivot
(
marks
for Month in (Jan, Feb, Mar, Apr)
) u
but all I get are months and years. Here is my sqlfiddle
I will always have 12 months, but I can have multiple data rows.
Can this be done without dynamic sql?

You're not going to get what you want with a single unpivot statement. Start with this unpivot:
with cte(Year, Month, Orders)
as
(
select Year, Month, Orders
from Data d
unpivot
(
orders
for Month in (Jan, Feb, Mar, Apr)
) u
)
I'm going to use those results in the next part, so I store it as a CTE. This query gives you results like this:
| YEAR | MONTH | ORDERS |
|------|-------|--------|
| 2013 | JAN | 90 |
| 2013 | FEB | 40 |
| 2013 | MAR | 30 |
etc...
I don't know what the numbers in your table represent, but I just called them orders. You can rename that column to whatever is appropriate. The next step is to pivot those results so that we can get the year displayed as columns:
select Month, [2013], [2014]
from cte
pivot
(
sum(orders)
for year in ([2013], [2014])
) p
order by datepart(mm, Month+'1900')
If you need to add more years, it should be obvious where to do that. Note the clever order by that sorts the months chronologically instead of alphabetically.
Here's a SQL Fiddle.

Related

Group Rows in group by though it contains NULL value in mysql / postgres

I have a table from where I am getting month names and some quantity measures.
Table Name = Month_Name
SELECT month_name,q1,q2 FROM month_name;
mysql> SELECT * FROM MONTH;
+------------+------+------+
| month_name | q1 | q2 |
+------------+------+------+
| January | 10 | 20 |
| March | 30 | 40 |
| March | 10 | 5 |
+------------+------+------+
Expected Output:
mysql> SELECT month_name ,SUM(q1),SUM(q2) FROM MONTH GROUP BY month_name;
+------------+---------+---------+
| month_name | sum(q1) | sum(q2) |
+------------+---------+---------+
| January | 10 | 20 |
| Febuary | 0 | 0 |
| March | 40 | 45 |
| April | 0 | 0 |
+------------+---------+---------+
Group by month will not print February and April since these 2 months are not present in base table. I do not want to use Union All since there will be performance issues with union All, Is there any other optimised approach to this.
You can use a calendar table which keeps track of all the month names which you want to appear in your report.
SELECT
m1.month_name,
SUM(q1) AS q1_sum,
SUM(q2) AS q2_sum
FROM
(
SELECT 'January' AS month_name UNION ALL
SELECT 'February' UNION ALL
SELECT 'March' UNION ALL
...
SELECT 'December'
) m1
LEFT JOIN month m2
ON m1.month_name = m2.month_name
GROUP BY
m1.month_name;
Note that while this solve your immediate problem, it is still not ideal, because we don't have any easy way to sort the months. A much better table design would be to maintain a date column. The month name is easily derived from the date.

Month to Month Growth (same column)

I have looked at my other solutions and none seem to work for me. I have a very simple question.
I have a table named foreclosures. It has 3 columns named: foreclosures_id, period, foreclosures. Period is the unique primary key. Datatype for foreclosures column is Decimal.
My current query is:
SELECT
MONTHNAME(period) AS Reported_Month,
YEAR(period) AS Reported_Year,
FORMAT(foreclosures, 0) AS Foreclosures,
(SELECT FORMAT(SUM(f2.foreclosures),0) FROM foreclosures f2
WHERE f2.period <= f1.period AND f1.foreclosures IS NOT NULL)
AS YTD_Total,
MONTHNAME(DATE_SUB(period, INTERVAL 2 MONTH)) AS Real_Month,
YEAR(DATE_SUB(period, INTERVAL 2 MONTH)) AS Real_Year
FROM foreclosures f1;
I am trying to produce the following "Growth" column. Please help?
+----------------+---------------+--------------+------------+
| Reported_Month | Reported_Year | Foreclosures | Growth % |
+----------------+---------------+--------------+------------+
| January | 2016 | 201 | |
| February | 2016 | 332 | 65.2% |
| March | 2016 | 240 | -27.7% |
| April | 2016 | 369 | 53.8% |
+----------------+---------------+--------------+------------+
You can get the foreclosures for the previous period using similar logic to the cumulative sum:
SELECT MONTHNAME(period) AS Reported_Month,
YEAR(period) AS Reported_Year,
FORMAT(foreclosures, 0) AS Foreclosures,
(SELECT f2.foreclosures
FROM foreclosures AS f2
WHERE f2.period < f.period AND
f.foreclosures IS NOT NULL
ORDER BY f2.period DESC
) as prev_foreclosures
FROM foreclosures f;
For the growth measure, just apply the arithmetic that you would use for that calculation.

2 columns. Merge duplicate values of A, sum duplicate values of B

I have a table that has 2 columns with data like this (from 1950 to 2015):
| Year | Count |
| 1994 | 10 |
| 1994 | 49 |
| 1994 | 2 |
| 1995 | 13 |
| 1995 | 6 |
I want my query result to be:
| Year | Count |
| 1994 | 61 |
| 1995 | 19 |
Things I have tried:
I began with a simple query like SELECT SUM(Count) FROM 'population' WHERE 'Year' = '1994' which was fine to bring a specific year but I wanted to fill an array with the population of every year in the database.
Doing something like SELECT Year, SUM(Count) FROM 'population' is closer to what I want except it just shown the first year only.
I'm not sure what terms I need to search up to get close to my answer. Union? I tried applying it but I just blerghed.
Try to use
SELECT year,SUM(Count) FROM 'population' group by year

SQL insert into select from - insert the id instead of the data

I need to populate my fact table with data from lds_placement table. I have selected the records and here is what it looks like:
fk1_account_id | fk3_job_role_id | salary | no_of_placements | YEAR
---------------------------------------------------------------------
10 | 3 | 165000 | 5 | 2010
10 | 3 | 132000 | 4 | 2011
10 | 3 | 132000 | 4 | 2012
20 | 2 | 990000 | 3 | 2010
20 | 2 | 132000 | 2 | 2011
20 | 2 | 132000 | 2 | 2012
I want to insert time_id from a different table called time_dim into the column year and not the actual year itself.
The time_dim table looks like this:
time_id | year
---------------
5 | 2015
1 | 2013
2 | 2010
3 | 2014
4 | 2012
6 | 2011
I need to insert into "year" column is actually:
year
2
6
4
2
6
4
Please give me the way to insert time_id instead of year in the table.
Here is the code I used to select the top-most table.
SELECT
fk1_account_id,
fk3_job_role_id,
Sum(actual_salary) AS salary,
Count(1) AS no_of_placements,
MAX(EXTRACT(YEAR FROM plt_estimated_end_date)) AS year
FROM lds_placement
GROUP BY fk1_account_id, fk3_job_role_id, EXTRACT(YEAR FROM plt_estimated_end_date)
ORDER BY fk1_account_id;
Use a left join if you want to capture records where year doesn't exist in time_dim. Else use inner_join.
select t.fk1_account_id,t.fk3_job_role_id,t.salary,t.no_of_placements
,d.time_id
from
(SELECT fk1_account_id, fk3_job_role_id, Sum(actual_salary) as salary, Count(1) as no_of_placements, MAX(EXTRACT(YEAR FROM plt_estimated_end_date)) AS YEAR
FROM lds_placement
GROUP BY fk1_account_id, fk3_job_role_id, EXTRACT(YEAR FROM plt_estimated_end_date)
)t
left join time_dim d
on t.year=d.year
order by t.fk1_account_id

Intersection of two MySql queries

I have a table where each data has a date when it was inserted in form of two columns, year and week:
+------+------+-------+
| Week | Year | Value |
+------+------+-------+
| 1 | 2014 | 5 |
| 5 | 2014 | 23 |
| 6 | 2014 | 12 |
| 7 | 2014 | 43 |
| 8 | 2014 | 4 |
| 9 | 2014 | 2 |
| 26 | 2013 | 21 |
| 27 | 2013 | 17 |
| 28 | 2013 | 42 |
| 31 | 2013 | 5 |
| ... | ... | .. |
+------+------+-------+
I need a query to get data that was inserted between two dates (year, week). I guess that it should be alternative to intersection of two queries, one with a starting date and the second with ending data, but I can't get it to work. Any help or sugestion?
Here is my shot but INTERSECT is not supported in MySql:
(SELECT SUM(Duration), Week, Type, Year
FROM UP26rotordowntime
WHERE (Year>=2013)
AND (Week >=01)
GROUP BY Week)
INTERSECT
(SELECT SUM(Duration), Week, Type, Year
FROM UP26rotordowntime
WHERE (Year<=2014)
AND (Week <=14)
GROUP BY Week)
You can put simple logic in WHERE conditions and use (year,week) pairs for GROUP BY:
SELECT SUM(Duration), Week, Type, Year
FROM UP26rotordowntime
WHERE Year = 2005 AND Week >= 10
OR Year BETWEEN 2006 AND 2013
OR Year = 2014 AND Week <= 14
GROUP BY Year,Week
If you have id the query is very simple:
SELECT SUM(Duration), Week, Type, Year FROM UP26rotordowntime
WHERE ID IN (
SELECT ID FROM UP26rotordowntime WHERE (Year>=2013) AND (Week >=01) )
OR ID IN (
SELECT ID FROM UP26rotordowntime WHERE (Year<=2014) AND (Week <=14))
GROUP BY Week
This should return you the intersect you need