How to do arithmetic group by quarter mySQL? - mysql

I have 2 tables which have not relation both of them.
Table of income
Id Category Nominal Description Date
---- -------- -------- -------- --------
1 ADD 10000 Q1 2020-03-05
2 DD 15000 Q2 2020-05-11
3 PAD 5000 Q3 2020-08-10
Table of outcome
Id Category Nominal Description Date
---- -------- -------- -------- --------
1 ADD 7000 Q1 2020-03-20
2 DD 10000 Q2 2020-06-02
3 PAD 2000 Q3 2020-08-28
So, I want to do subtraction of nominal from income with nominal from outcome group by quarter.
Here is my query :
CREATE view Total AS
SELECT QUARTER(outcome.date) AS Qperiod, income.nominal-outcome.nominal AS remain
FROM income, outcome
GROUP BY YEAR(outcome.date), QUARTER(outcome.date)
this result shown below, it describe that first row in income table subtraction by all outcome nominal.
Qperiod remain
---- --------
1 3000
2 0
3 8000
Can anyone help me to solve this?

You have no JOIN condition in your query, so each row of income gets matched to every row of outcome. Since you have no aggregation function, this effectively means that a random row from outcome is subtracted from each row of income. You should use modern, explicit JOIN syntax and put in the appropriate JOIN condition, which is that the year and quarter are the same in both tables:
CREATE view Total AS
SELECT QUARTER(outcome.date) AS Qperiod, income.nominal-outcome.nominal AS remain
FROM income
JOIN outcome ON YEAR(outcome.date) = YEAR(income.date)
AND QUARTER(outcome.date) = QUARTER(income.date)
GROUP BY YEAR(outcome.date), QUARTER(outcome.date)
Output:
Qperiod remain
1 3000
2 5000
3 3000
Demo on SQLFiddle

Related

Calculate a log of two columns into total amount

I have a log of points that I want to import somewhere else as (email - total points).
I'm trying to figure out a query that does the following:
Calculate points for EACH user_id, and merging those user_ids so they won't be duplicated.
After that, I need to replace the USER_IDs with corresponding emails in another table:
Source Table
USER_ID Points
------- ------
1 10
2 30
3 50
1 -5
2 5
3 -40
Desired result
USER_ID Points
------- ------
1 5
2 35
3 10
STEP 2
Another Source Table
USER_ID Email
------- ------
1 one#one.com
2 two#two.com
3 three#three.com
Final Desired result:
USER_ID Total Points
------- -----------
one#one.com 5
two#two.com 35
three#three.com 10
To pretify your query you can write like that :
select oc_customer.email,
sum(oc_customer_reward.points) as Total_points
from oc_customer_reward
inner join oc_customer using(customer_id)
group by customer_id

add Total to the duplicate values

I have requirement like
claimnumber -- amount -- category
----------- ------ --------
123 -- 10 -- A
123 -- 10 -- B
456 -- 10 -- C
456 -- 10 -- B
I want report to group data like below,
distinct claim nos- 2
total charge for them is 20
Total Count -- Total Amount
----------- ------------
2 -- 20
How is it possible with ssrs.?
This should work:
select
count(claimnumber) as [total count],
sum(amount) as [total amount]
from tablename
group by claimnumber

get 1st and 2nd highest vlaue rows in case of similar values

I have a table with the columns : id, status, value.
id status value
-- ------ -----
1 10 100
2 10 100
3 10 60
4 11 20
5 11 15
6 12 100
7 12 50
8 12 50
I would like to get the id and value of the first and second highest valued rows, from each status group. My table should have the following columns:
status, id of the first highest value, first highest value, id of second highest value, second highest value.
I should get:
status 1stID 1stValue 2ndID 2ndValue
------ ----- -------- ----- --------
10 1/2 100 2/1 100
11 4 20 5 15
12 6 100 7/8 50
I tried all kinds of solutions, but I couldn't find a solution for same-value 1st s (two rows with the same value, which happened to be the highest in that status group) or same-value seconds.
For example, in case of two rows sharing the highest value in their status group, this not-so-elegant query will return two rows with the same status, different 1sts and same 2nd:
SELECT 2nds.status, 1sts.id AS "1stID",1sts.value AS "1stValue",
2nds.id AS "2ndID",2nds.value AS "2ndValue"
FROM
(SELECT v.* FROM
(SELECT status, MAX(value) AS "SecMaxValue" FROM table o
WHERE value < (SELECT MAX(value) FROM table
WHERE status = o.status
GROUP BY status) AS m
INNER JOIN table v
ON v.status = m.status AND v.value = m.SecMaxValue) AS 2nds
INNER JOIN
(SELECT v.* FROM
(SELECT status, MAX(value) AS maxValue FROM table
GROUP BY status) AS m
INNER JOIN table v
ON v.status = m.status AND v.value = m.MaxValue) AS 1sts
ON 1sts.status = 2nds.status ;
This query will give me:
status 1stID 1stValue 2ndID 2ndValue
------ ----- -------- ----- --------
10 1 100 3 60
10 2 100 3 60
11 4 20 5 15
12 6 100 7 50
12 6 100 8 50
To conclude, I would like to find a solution in which:
a. if there are two rows with the highest value the query puts the details one of them in the column of the 1st and the details of other in 2nd (no mather which)
b. if there are two rows with the second highst value it puts the highest in its place and one of the seconds in the second place.
Is there a way to change the query above? someone has a nicer solution?
I came across several 1st and 2nd queries but they had the same problem - for example this solution: Finding the highest n values of each group in MySQL. it does not deliver 1st and 2nd in the same row, but the main problem it provides only one of the firsts.
Thanks
After spent a lot of time, finally I found a solution for above problem. Please try it out:
select 1st.status as Status,
SUBSTRING_INDEX(1st.id,'/',1) as 1stID,
1st.value as 1stValue,
(case when locate('/',1st.id) > 0 then SUBSTRING_INDEX(1st.id,'/',-1)
else 2nd.id
end) as 2ndID,
(case when locate('/',1st.id) > 0 then 1st.value
else 2nd.value
end) as 2ndValue
from
(
(select status, SUBSTRING_INDEX(Group_concat(id separator '/'),'/',2) as id,value
from t1
where (status,value) in (select status,value
from t1
group by status
having max(value))
group by status) 1st
inner join
(select status,id,value
from t1
where (status,value) not in (select status,value
from t1
group by status
having max(value))
group by status,value
order by status,value desc) 2nd
on 1st.status = 2nd.status)
group by 1st.status;
Just replace t1 with your tablename and it should work like a charm.
Click here for Updated Demo
If you have any doubt(s), feel free to ask.
Hope it helps!

Subquery to display one column in a Table as two separate columns

I have two tables
Ledgers Table
==============
ledger_id ledger_name open_bal close_bal dr_bal cr_bal
--------- --------- --------- --------- ------ ------
1 Bank A/C 0 5000 5000 0
2 Capital A/C 0 -50000 0 50000
3 Cash A/C 0 30700 53500 22800
Transactions Table
==============
trans_id trans_date ledger_id ledger_name amount trans_type
--------- --------- --------- --------- --------- ---------
1 2004-01-01 3 Cash A/C 50000 Dr
2 2004-01-01 2 Cap A/C 50000 Cr
3 2004-01-02 9 Purchase A/C 10000 Dr
These are my tables, what am trying to achieve is to get the ledgers balances for a particular month. Here am not trying to insert data into a table.
Using the above two tables i just need to query and out put the result as
Desired Output
==============
ledger_id ledger_name amount trans_type As Debit trans_type as Credit
--------- --------- ------ --------- ---------
3 Cash A/C 50000 Dr Null
2 Capital A/C 50000 Null Cr
So trans_type field here is displayed separately DR as Debits and Cr as Credits. This is what I want to achieve.
What i have tried till now is joining the transaction table with the ledgers! But i have failed to get the desired output just by querying these two tables.
This is what i have tried,
SELECT tr.trans_date, tr.amount, tr.ledger_id, l.ledger_name, tr.trans_type
FROM tbl_transaction tr LEFT JOIN tbl_ledgers l
ON l.ledger_id = tr.ledger_id WHERE trans_date BETWEEN '2004-01-01' AND '2004-01-31';
So i basically want to know how can we achieve this? trans_type column split into two "Dr" separate and "Cr" separate?
You may use a CASE to inspect the actual value of trans_type and display it, doing so once for each alias Debit, Credit. This gets unwieldy with more than a few values, but since you have just two it is a simple method.
SELECT
tr.ledger_id,
l.ledger_name,
tr.amount,
CASE WHEN tr.trans_type = 'Dr' THEN tr.trans_type ELSE NULL END AS Debit,
CASE WHEN tr.trans_type = 'Cr' THEN tr.trans_type ELSE NULL END AS Credit
FROM
tbl_transaction tr
LEFT JOIN tbl_ledgers l ON l.ledger_id = tr.ledger_id
WHERE
trans_date BETWEEN '2004-01-01' AND '2004-01-31';
Here is a demonstration: http://sqlfiddle.com/#!9/f5623/2
Note that this uses a LEFT JOIN, resulting in ledger_id = 9 being returned in the demonstration (different than your example). Changing it to an INNER JOIN would correct that. (http://sqlfiddle.com/#!9/f5623/3)
Try this:
SELECT IIf([trans_type]='dr',[trans_type],"") AS debit, IIf([trans_type]='cr',[trans_type],"") AS credit
FROM tr;
This code creates an IF statement, to determine whether the [trans_type] field is 'cr' or 'dr' and returns the value in trans_type ('cr' or 'dr') if the condition is met or returns a blank ("") when the condition is not met.

SQL group rows by student_id on unique marks for any course

I have a SQL table like this:
Student_id mark course
---------- -------- ---------
1 15 math
1 15 physics
2 15 math
2 16 physics
and want to produce this output:
Student_id mark count
---------- -------- ---------
1 15 2
2 15 1
2 16 1
count is number of single unique mark for each student (on any number of courses)
SELECT `student_id`, `mark`, count(1) AS `count`
FROM `the_table`
GROUP BY `student_id`, `mark`
;
If you want the results in a certain order, for now "GROUP BY" in MySql does it, but I've recently heard it's been deprecated in the latest versions so you might want to add an ORDER BY after the GROUP BY to future-proof a little.