Select flat data into table format - mysql

I have a table with a schema similar to the following
id | year | month | amount ...
x | 2011 | 12 | 312
x | 2011 | 12 | 213
x | 2012 | 1 | 123
x | 2012 | 1 | 123
x | 2012 | 2 | 123
...
I want to know if it would be possible (via mysql) to output something like
year | 1 | 2 | 3 | ....
2011 | 321 | 231 | ...
2012 | 246 | 123 | ...
So doing a group by year, month, sum(amount) and output one "row" per year with the individual months as the columns.
Thanks

You would want to use a pivot table of sorts. If you are only dealing with months something like the below SQL should work for you. I only did the first three months but the rest are the same.
SELECT year, SUM(IF(month=1,amount,0)) As '1', SUM(IF(month=2,amount,0)) As '2', SUM(IF(month=3,amount,0)) As '3' FROM mytable GROUP BY year

Related

How to merge few rows into one row when it has a same value in mysql

I am getting the following table,
+-------------+-----------+------------+--------------------+
| quantity | year | month | category |
+-------------+-----------+------------+--------------------+
| 122 | 2012 | 7 | 15 |
| 100 | 2012 | 7 | 25 |
| 1029| 2012 | 7 | 10 |
| 212 | 2012 | 7 | 0 |
+-------------+-----------+------------+--------------------+
But I want to get it as how we can merge cells in excel
+-------------+-----------+------------+--------------------+
| quantity | year | month | category |
+-------------+-----------+------------+--------------------+
| 122 | | | 15 |
| 100 | 2012 | 7 | 25 |
| 1029| | | 10 |
| 212 | | | 0 |
+-------------+-----------+------------+--------------------+
checking if this is possible in MySQL?
You might find that aggregation is what you really want:
select year, month, group_concat(quantity), group_concat(category)
from t
group by year, month;
This produces one row for each year/month combination with a list of the quantities and categories. You can get fancy and use a newline to separate the values. They would appear on multiple "lines" but still be one "row".
Note that the ordering is not specified in the question. And without an order by in the group_concat() the values for the two columns might not be in the same order as the original data.

MySQL: Tagging 1 to each unique occurence during SELECT query

Sample table tbl_name:
| ID | Name | Month | Quarter | Year |
| 1 | A | Jan | 1 | 2019 |
| 1 | A | Feb | 1 | 2019 |
| 2 | B | May | 2 | 2019 |
| 3 | C | May | 2 | 2018 |
Hi, this is the table I extract using SELECT query. I can find the distinct name per year using SELECT distinct name, year FROM tbl_name; But I'm trying to add a column during SELECT query to identify or count the unique occurrence per year of the name.
Expected:
| ID | Name | Month | Quarter | Year | Unique Count |
| 1 | A | Jan | 1 | 2019 | 1 |
| 1 | A | Feb | 1 | 2019 | 0 |
| 2 | B | May | 2 | 2019 | 1 |
| 3 | C | May | 2 | 2018 | 1 |
I tried splitting into two queries - one select everything; the other select just distinct and join them together but that will introduce duplicates. Is there a way to do this using SQL?
If you are running MySQL 8.0, you can use row_number() to flag the appearance of a name in a year:
select
t.*,
(row_number() over(
partition by name, year
order by str_to_date(concat(year, '-', month), '%Y-%b')
) = 1) unique_count
from mytable t
Note: do consider fixing the storage strategy of your date columns. Rather than splitting the information over several columns, you would better have a unique column in the relevant DATE datatype to store that information. That would save you the pain of recomposing the date when you need it.
Demo on DB Fiddle:
ID | Name | Month | Quarter | Year | unique_count
-: | :--- | :---- | ------: | ---: | -----------:
1 | A | Feb | 1 | 2019 | 1
1 | A | Jan | 1 | 2019 | 0
2 | B | May | 2 | 2019 | 1
3 | C | May | 2 | 2018 | 1
You can try this below logic-
DEMO HERE
WITH your_table(ID,Name,Month,Quarter,Year)
AS
(
SELECT 1,'A','Jan',1,2019 UNION ALL
SELECT 1,'A','Feb',1,2019 UNION ALL
SELECT 2,'B','May',2,2019 UNION ALL
SELECT 3,'C','May',2,2018
)
,CTE AS
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Quarter,Year) RN
FROM your_table
)
SELECT ID,Name,Month,Quarter,Year,
CASE WHEN RN = 1 THEN 1 ELSE 0 END Unique_Count
FROM CTE
Output is-
ID Name Month Quarter Year Unique_Count
1 A Jan 1 2019 1
1 A Feb 1 2019 0
2 B May 2 2019 1
3 C May 2 2018 1

Get Top 5 For Each Metric For Each Month For Each Item

I'm having trouble creating the query to break my now daily, table into a top monthly.
I have a table with the following structure (note the headers are actually all caps):
Start_Date | Month| Item | Location | ... | Quantity | Sales |
-----------|------|------|----------|-----|----------|-------|
8/6/19 | 08 | A | USE | ... | | |
8/6/19 | 08 | B | USE | ... | | |
8/6/19 | 08 | C | USW | ... | | |
8/6/19 | 08 | D | USW | ... | | |
8/5/19 | 08 | A | USE | ... | | |
8/5/19 | 08 | B | USE | ... | | |
8/5/19 | 08 | C | USW | ... | | |
8/5/19 | 08 | D | USW | ... | | |
.....
7/1/19 | 07 | D | USW | ... | | |
Every date has the metrics above, there's 4 rows per day due to the 4 different items, which I want -- comparing by item. My goal is to now break this out to a monthly table, with the goal of taking the top 5 in each category (Quantity, Sales, etc.) for that month and getting the AVG of that.
Example: Item A
8/6/19: Quantity = 500 | Sales = 100
8/5/19: Quantity = 478 | Sales = 130
8/4/19: Quantity = 366 | Sales = 113
8/3/19: Quantity = 678 | Sales = 90
8/2/19: Quantity = 594 | Sales = 92
8/1/19: Quantity = 500 | Sales = 105
Note: There's data for the other items B, C and D respectively.
My goal was to take the top 5 for each category and present that at the monthly level:
Results:
| Month| Item | Location | ... | Quantity | Sales |
|------|------|----------|-----|----------|-------|
| 08 | A | USE | ... | 550 | 108 |
| 08 | B | USE | ... | | |
| 08 | C | USW | ... | | |
| 08 | D | USW | ... | | |
| 07 | A | USE | ... | | |
| 07 | B | USE | ... | | |
| 07 | C | USW | ... | | |
| 07 | D | USW | ... | | |
Quantity = 550 was taken from 8/1 -> 8/6 Quantity adding the top 5 (1, 2, 3, 5, 6) and dividing by 5 (AVG of top 5). Then Sales was 1, 2, 4, 5, 6.
So obviously I need to query each category (Quantity, Sales, etc.) separately and then UNION the results together. I'm just struggling with how to even get the TOP 5 of a specific category.
I've done some searching on Stack and Google for how to obtain the Top5 in a query. I see some threads that suggest you can actually use TOP(5) which doesn't work for me. LIMIT 5 only limits the results to 5, and I'm unable to use LIMIT in a subquery w/ the most recent version of SQL. Ordering and using a simple statement like "number <= 5" won't work since the months are different in the later rows of the dataset... I'm able to get the AVG for all the data for a specific month by simply just SELECTing the AVG() and GROUPing by ITEM but I want to project a top 5/10 which I've been unable to figure out.
Thanks for your assistance. I could throw out some of the queries I've tried but none are even close as I've been trying to use LIMIT.
You can use window functions:
select month, item,
avg(case when seqnum_q <= 5 then quantity end) as quantity_top5,
avg(case when seqnum_s <= 5 then sales end) as sales_top5
from (select t.*,
row_number() over (partition by item, month order by quantity desc) as seqnum_q,
row_number() over (partition by item, month order by sales desc) as seqnum_s
from t
) t
group by month, item;
Note: Using month without year is discomfiting. Either year should be included in the logic, or you should be clear that the data is only for one year.

Is it possible to add to an existing value in a cell in mysql?

I have a table which saves monthly values, but also a value for the complete year. Is is possible to add to the yearly value whenever I insert a value for a month?
I want to avoid loading the value first, adding to it in the server-code and writing it again.
You can write a trigger and insert value in the years table when any value is inserted in the Month table like
CREATE TRIGGER tr_month ON monthly_table
AFTER INSERT
AS
BEGIN
UPDATE year_table
SET // insert your values here
FROM inserted
WHERE monthly.id = inserted.id; // something like that, I am not sure about your structure thats why cannot add exact syntax
END
GO
Your best approach to this is avoiding redundant data in your table. When you need year totals, SELECT them.
You didn't tell us your table definition, so I will guess. The table months contains
year int (for example, 2019)
month int (1-12)
value number
You can get the details of this the obvious way: `
SELECT year, month, value FROM months;
You can get the details and the yearly sums this way
SELECT year, month, SUM(value) value
FROM months
GROUP BY year, month WITH ROLLUP;
The result set for this query looks like the other result set, but also contains sums. It looks like this:
| year | month | value |
| ---- | ----- | ----- |
| 2018 | 1 | 100 | detail month values...
| 2018 | 2 | 140 |
| 2018 | 3 | 130 |
| 2018 | 4 | 190 |
| 2018 | 5 | 120 |
| 2018 | 6 | 180 |
| 2018 | 7 | 130 |
| 2018 | 8 | 140 |
| 2018 | 9 | 150 |
| 2018 | 10 | 200 |
| 2018 | 11 | 230 |
| 2018 | 12 | 300 |
| 2018 | | 2010 | yearly sum for 2018 (month is NULL)
| 2019 | 1 | 100 |
| 2019 | 2 | 130 |
| 2019 | 3 | 160 |
| 2019 | 4 | 140 |
| 2019 | 5 | 190 |
| 2019 | 6 | 240 |
| 2019 | | 960 | yearly sum for 2019 (month is NULL)
| | | 2970 | total sum (both month and year are NULL)
View on DB Fiddle
Why is this a good process?
you need to store no extra data.
it works correctly even if you update or delete rows in your table.
it's fast: SQL is made to do this kind of thing.
Just solved it by adding the values client side, this also saves computing time on the server.

Add a total column in an access transform statement

I have the following statement
TRANSFORM Sum(revenue) AS sum_revenue
SELECT year
FROM revenues
GROUP BY year
PIVOT month;
Which gives me a table like
year | 1 | 2 | ... | 11 | 12
------------------------------------------
2013 | 5000 | 6000 | ... | 5000 | 6000
2014 | 5000 | 6000 | ... | 5000 | 6000
2015 | 5000 | 6000 | ... | 5000 | 6000
I would like to add a total column at the end like this:
year | 1 | 2 | ... | 11 | 12 | total
--------------------------------------------------
2013 | 5000 | 6000 | ... | 5000 | 6000 | 75000
2014 | 5000 | 6000 | ... | 6000 | 6000 | 80000
2015 | 5000 | 6000 | ... | 6000 | 7000 | 85000
How can I achieve this?
If you had used the Query Wizard it should have built it for you by default, unless you have checked it not to include "Totals" column. However you can manually add it, by going to the design view and adding this,
TRANSFORM
Sum(revenue) AS sum_revenue
SELECT
[year],
Sum(revenue) As [Total Of SumRevenue]
FROM
revenues
GROUP BY
[year]
PIVOT
[month];
PS: I played with the query, I get the right result. Not sure why yours is not !