How to loop table rows and get header - value pair on MySQL - mysql

For example I got table like this:
ID | GroupID | Fee | Discount
-----------------------------
1 | 20 | 60 | 15
2 | 21 | 55 | 42
I want to loop each row and get header - value pair like this:
From row 1: GroupID - 20, Fee - 60, Discount - 15
From row 2: GroupID - 21, Fee - 55, Discount - 42
So basically, this is my old table and I want to convert it to the new one. The new one will look like this:
ID | GroupID | Type | Value
-------------------------------
1 | 20 | Fee | 60
2 | 20 | Discount | 15
3 | 21 | Fee | 55
4 | 21 | Discount | 42

SELECT
t.ID,
t.GroupID,
a.Type,
IF(a.Type = 'Fee', Fee, Discount) AS `Value`
FROM t
CROSS JOIN (SELECT 'Fee' AS Type UNION SELECT 'Discount') a
ORDER BY ID, GroupID;
see it working live here

Related

How to filter a table based on a specific column value of another column?

Hello I have a table here
---------------------
ID | PARTY | Name.Id
---------------------
1 | IND | 12
2 | IND | 13
3 | CUST | 14
4 | CUST | 15
5 | CUST | 16
6 | IND | 17
---------------------
I want to return the whole table but filter 'CUST' which has value 15 and 16 in the column 'Name.Id'
The result should look something like this
---------------------
ID | PARTY | Name.Id
---------------------
1 | IND | 12
2 | IND | 13
3 | CUST | 14
4 | IND | 17
---------------------
I tried using where statement on the 'Name.Id' but it returns only those rows which has a value 15 and 16.
whole table but filter 'CUST' which has value 15 and 16 in the column 'Name.Id'
It sounds like you want a where clause like:
WHERE NOT (party = 'CUST' and name.id IN(15,16))
But CUST seems redundant from your sample data, i.e. you could get away with simplifying it to just WHERE name.id NOT IN (15,16)
Can you give this a whirl?
select *
from t
where (PARTY, Name.Id) not in (
select 'CUST', 15
union
select 'CUST', 16
)
select *
from t
where Party not in
(select Party from t
where Party="CUST"
and (Name.Id=15 or Name=16));
I think I got the solution here
select *
from t
where Party <> 'CUST'
or (Party = 'CUST'
and Name.Id = 14))

Query to lookup reference tables on sum the result

I am new to SQL, would like to have your suggestions on how to solve this problem,
I have the sales information by type
I want to sum the Prices of certain references by Type and based on the resulting sum, fetch the values from another table and populate in the Output Column.
Group Type 100000 200000 300000
1 A 1 2 3
1 B 0 1 1
2 T 2 2 4
2 U 0 2 2
3 V 2 2 3
4 N 1 1 1
From the above table 2 we find the TYPE A and B belong to same group - Group 1. So in the first table, the query should sum Prices of the references belonging to the Group 1. If the sum is >100000 and <=200000 then based on the type the corresponding value must be chosen.
Incase the sum of Prices based on group is less than 100000 or the type not found in Table 2 then it should take the values from the below table
[+------+----+---+
| Type | 1 | 2 |
+------+----+---+
| A | 50 | 2 |
| B | 60 | 5 |
| C | 65 | 2 |
| D | 65 | 3 |
| E | 65 | 4 |
+------+----+---+][3]
Thus the final output for the above datasheet would be like below,
Order ID Reference Type Price Output
101 AAA A 500000 3
101 AAB B 100000 1
101 ABC C 20000 67
101 DCE B 50000 1
101 BOD D 200000 68
101 ZYZ E 200000 69
102 AAA A 20000 52
So for the first line, its TYPE A and Type A is present under Group 1 and in Group1 we also have Type 2. So for the same order ID 101 , the overall Sales of Type A and B is 650000 > 300000, therefore for Type A we chose the value 3 from the table 2. Since Type C is not present in Table 2, I went to Table 3 and added the two values and so on
Sorry for the long post. Hope my question is clear? Would like to have your expert opinion.
Thanks,
SS
Join all tables and make sure you do LEFT JOIN as we want to keep records from the first table even we don't have corresponding data in the second or third table.
For total count, give priority to the second table, use case when to verify in which range this mrp field is falling. If lies within a range pick count from the second table otherwise pick count from the third table.
SELECT
s.order_id,
s.reference,
s.`type`,
s.mrp,
#a:= IFNULL(g_total.Total, s.mrp) AS MRP_Total, -- #a variable to use it in CASE WHEN clause
CASE
WHEN #a > 100000 AND #a <= 200000 AND sg.`type` IS NOT NULL THEN sg.price_100000
WHEN #a > 200000 AND #a <= 300000 AND sg.`type` IS NOT NULL THEN sg.price_200000
WHEN #a > 300000 AND sg.`type` IS NOT NULL THEN sg.price_300000
ELSE tp.price_1 + tp.price_2
END Total
FROM sales s
LEFT JOIN sales_group sg ON s.`type` = sg.`type`
LEFT JOIN type_prices tp ON s.`type` = tp.`type`
LEFT JOIN (
SELECT
s.order_id, sgg.`group`, SUM(mrp) as Total
FROM sales s
INNER JOIN sales_group sgg ON s.`type` = sgg.`type`
GROUP BY s.order_id, sgg.`group`
) AS g_total -- Temp table to find total MRP, order and group wise
ON s.order_id = g_total.order_id AND sg.`group` = g_total.`group`
ORDER BY s.order_id, s.`type`;
Output:
sales
---
| order_id | reference | type | mrp | MRP_Total | Total |
---------------------------------------------------------
| 101 | AAA | A | 500000 | 650000 | 3 |
| 101 | DCE | B | 50000 | 650000 | 1 |
| 101 | AAB | B | 100000 | 650000 | 1 |
| 101 | ABC | C | 200000 | 200000 | 67 |
| 101 | BOD | D | 200000 | 200000 | 68 |
| 101 | ZYZ | E | 200000 | 200000 | 69 |
| 102 | AAA | A | 20000 | 20000 | 52 |
Note: sg.type IS NOT NULL is added in CASE WHEN clause because if we don't have any mapping in the second table, we should move to ELSE part which refers to the third table.

Average on group totals i Reporting Services 2012

I am trying to make an average on group totals in SQL Server Reporting Services 2012 with a tablix.
Seems to be the same problem that this question is struggeling with...however there are no responses: SQL 2005 Reporting Services - Wrong Average Values
My tablix looks like the following:
+-------+---------+-----------+-----------+
| | | Product A | Product B |
+-------+---------+-----------+-----------+
| | Average | ??? | ??? |
| | | | |
| Week1 | | 550 | 175 |
| | Day 1 | 250 | 100 |
| | Day 2 | 200 | 50 |
| | Day 3 | 100 | 25 |
| | | | |
| Week2 | | 600 | 240 |
| | Day 1 | 300 | 200 |
| | Day 2 | | 30 |
| | Day 3 | | 10 |
+-------+---------+-----------+-----------+
The average should only be calculated for the week-totals. I.e. for Product A the average should be (550+600)/2=575.
My formula for average looks like the following:
=Avg(Fields!WeekTotal.Value)
however this gives a wrong result - my guess is that it takes the days into account aswell?
The question gets harder, because sometimes i do not have the Days sale, in which case the WeekTotal will be a prediction
My dataset, which comes from a SQL Server table, contains the following data:
+---------+------+-----+-----------+----------+
| Product | Week | Day | WeekTotal | DayTotal |
+---------+------+-----+-----------+----------+
| A | 1 | 1 | 550 | 250 |
| A | 1 | 2 | 550 | 200 |
| A | 1 | 3 | 550 | 100 |
| B | 1 | 1 | 175 | 100 |
| B | 1 | 2 | 175 | 50 |
| B | 1 | 3 | 175 | 25 |
| A | 2 | 1 | 600 | 300 |
| A | 2 | 2 | 600 | NULL |
| A | 2 | 3 | 600 | NULL |
| … | … | … | … | … |
+---------+------+-----+-----------+----------+
Any help is appreciated!
You can simply sum all the detail values (at day level) then divide by the number of unique weeks. Something like this...
=Sum(Fields!DayTotal.Value)/CountDistinct(Fields!Week.Value)
In your sample CountDistinct(Fields!Week.Value) will return 2
This all assumes you have a simple Matrix as your report design something like this...
The expression is the expression above to calculate the average.
Please note in my sample data I have more detail so I grouped by day as well as week. In your case you don;t need to group by day (but it will still work if you do)
Also, you don't need the week total as you can simply sum the day totals at the week group level.
UPDATE based on OP comments:
As you need to use the actual week totals in your data (which are not always just a sum of the day totals as I thought previously) we will have to edit the dataset and do a bit of the work there.
Below is the code is used to generate your sample dataset.
DECLARE #t TABLE (Product varchar(1), [Week] int, [Day] int, WeekTotal int, DayTotal[int])
INSERT INTO #t
VALUES
('A', 1, 1, 550, 250),
('A', 1, 2, 550, 200),
('A', 1, 3, 550, 100),
('B', 1, 1, 175, 100),
('B', 1, 2, 175, 50),
('B', 1, 3, 175, 25),
('A', 2, 1, 600, 300),
('A', 2, 2, 600, NULL),
('A', 2, 3, 600, NULL)
SELECT
*
, CAST(WeekTotal as float) / COUNT(*) OVER(PARTITION BY Product, [Week]) as WeekTotalAverage
FROM #t
Notice that I now return an extra column WeekTotalAverage (I know, poor name!). What this does is give us an average per product/week of the weektotal, we can then simply replace the daytotal in the previous expression with this field. The dataset output looks like this.
Product Week Day WeekTotal DayTotal WeekTotalAverage
A 1 1 550 250 183.333333333333
A 1 2 550 200 183.333333333333
A 1 3 550 100 183.333333333333
A 2 1 600 300 200
A 2 2 600 NULL 200
A 2 3 600 NULL 200
B 1 1 175 100 58.3333333333333
B 1 2 175 50 58.3333333333333
B 1 3 175 25 58.3333333333333
I've CAST the result of this column to a float or we end up losing fractions of the numbers when the result is recurring. So in this case product B week 1 has three entries so we take the week total (165) and divide by 3 to give us 58.33 recurring.
The report design changes a little too. It now looks like this.
The week total is now just [WeekTotal] (previously it was [SUM(DayTotal)]) and the expression for the Average is now..
=Sum(Fields!WeekTotalAverage.Value) / CountDistinct(Fields!Week.Value)
In the matrix, there are two row groups, the first groups by Week and the second (child group) is grouped by Day. The column group is grouped on Product.
My solution would be to create a second recordset that has only one weekly total entry for every week, and then take the average from that scope rather than the recordset used for the detailed part of the report.

MySQL - Student grade list, replace small grade by bonus grade

SELECT TestID, Grade FROM tests_points;
Returns:
+--------+-------+
| TestID | Grade |
+--------+-------+
| 10 | 125 |
| 11 | 110 |
| 12 | 100 |
| 13 | 75 |
| 14 | 50 |
| 15 | 65 |
| 16 | 70 |
| 17 | 100 |
| 18 | 100 |
+--------+-------+
But, tests ID 17 and 18 are "bonus tests", so I need replace the two lower grades by these two ones, and return the SUM of all grades.
So, how I can "replace"the two lower grades (From TestID 14 and 15) by testID 17 and 18 grades.
The "correct grade list" would be:
+--------+-------+
| TestID | Grade |
+--------+-------+
| 10 | 125 |
| 11 | 110 |
| 12 | 100 |
| 13 | 75 |
| 14 | 100|
| 15 | 100|
| 16 | 70 |
+--------+-------+
In the end I just need the SUM of all grades, fixing the lower grades.
SELECT SUM(Grade) FROM tests_points;
How can I do that?
This is what you need!
select testID, GREATEST(
IF(
grade = (select min(grade) from test_points),(select grade from test_points where testID = 17),
grade)
,
IF(grade = (select min(grade) from test_points WHERE grade > ( SELECT min(grade) FROM test_points))
,(select grade from test_points where testID = 18),
grade)) as Score
from test_points
where testID not in(17, 18)
Demo on SQLfiddle.com
the following query will get probably get what you want..
SELECT ttal-garba FROM
(SELECT sum(c.grade) as garba FROM (SELECT * from tests_points where testid < 17 order by grade asc limit 2) as c) as a,
(SELECT sum(grade) as ttal FROM tests_points) as b
(I know.. hard coding everything is bad ..)
If your just trying to add up all the values except the bottom two you can do something lik,e this.
First add a new field lets say test_type:
UPDATE test_points
SET test_type = 'test';
Now we can just select using group by the sum. For example
SELECT test_type, (SUM(Grade) OVER (ORDER BY Grade DESC))
FROM test_Points
LIMIT 7;
This will return the sum of all values, but it will show 7 columns. If you want only the total, Throw a group by into the mix, and it will narrow it down to one field, or just select the 7th row number.

MySQL - Get row with the maximum HISTORY ID for COMPONENT IDs in non-existing months

I have a table INVENTORY which consists of inventory items. I have the following table structure:
INSTALLATION_ID
COMPONENT_ID
HISTORY_ID
ON_STOCK
LAST_CHANGE
I need to obtain the row with the max HISTORY ID for records for which the spcified LAST_CHANGE month doesn't exist.
Each COMPONENT_ID and INSTALLATION_ID can occur multiple times, they are distinguished by their respective HISTORY_ID
Example:
I have the following records
COMPONENT_ID | INSTALLATION_ID | HISTORY_ID | LAST_CHANGE
1 | 100 | 1 | 2013-01-02
1 | 100 | 2 | 2013-02-01
1 | 100 | 3 | 2013-04-09
2 | 100 | 1 | 2013-02-22
2 | 100 | 2 | 2013-03-12
2 | 100 | 3 | 2013-07-07
2 | 100 | 4 | 2013-08-11
2 | 100 | 5 | 2013-09-15
2 | 100 | 6 | 2013-09-29
3 | 100 | 1 | 2013-02-14
3 | 100 | 2 | 2013-09-23
4 | 100 | 1 | 2013-04-17
I am now trying to retrieve the rows with the max HISTORY ID for each component but ONLY for COMPONENT_IDs in which the specifiec month does not exists
I have tried the following:
SELECT
INVENTORY.COMPONENT_ID,
INVENTORY.HISTORY_ID
FROM INVENTORY
WHERE INVENTORY.HISTORY_ID = (SELECT
MAX(t2.HISTORY_ID)
FROM INVENTORY t2
WHERE NOT EXISTS
(
SELECT *
FROM INVENTORY t3
WHERE MONTH(t3.LAST_CHANGE) = 9
AND YEAR(t3.LAST_CHANGE)= 2013
AND t3.HISTORY_ID = t2.HISTORY_ID
)
)
AND INVENTORY.INSTALLATION_ID = 200
AND YEAR(INVENTORY.LAST_CHANGE) = 2013
The query seems to have correct syntax but it times out.
In this particular case, i would like to retrieve the maximum HISTORY_ID for all components except for those that have records in September.
Because I need to completely exclude rows by their month, i cannot use NOT IN, since they will just suppress the records for september but the same component could show up with another month.
Could anybody give some pointers? Thanks a lot.
If I understand correctly what you want you can do it like this
SELECT component_id, MAX(history_id) history_id
FROM inventory
WHERE last_change BETWEEN '2013-01-01' AND '2013-12-31'
AND installation_id = 100
GROUP BY component_id
HAVING MAX(MONTH(last_change) = 9) = 0
Output:
| COMPONENT_ID | HISTORY_ID |
|--------------|------------|
| 1 | 3 |
| 4 | 1 |
If you always filter by installation_id and a year of last_change make sure that you have a compound index on (installation_id, last_change)
ALTER TABLE inventory ADD INDEX (installation_id, last_change);
Here is SQLFiddle demo