How to aggregate values inside a column in SQL? - mysql

I could not solve this question, any help?
Batsman_detail table has the columns ,
[Opposition (varchar), runs(int), dismissal(varchar), venue(varchar)]:
opposition column has may countries in it.
runs column has the amount of runs scored by the batsman in that match.
dimissal column has any of the following values caught/bowled/run out/not out.
venue column has the stadium name where the match is held.
Now you should write a query such that the output table must have that number of times the batsman had gone out in a type of dismissal against the countries Pakistan,Australia and Srilanka (Original table contains many countries.),
Example output format :
opposition caught bowled runout notout
Pakistan 3 5 7 9
Australia 0 4 4 1
Sri Lanka 4 2 3 4

You seem to want conditional aggregation:
select opposition,
sum(case when dismissal = 'caught' then runs else 0 end) caught,
sum(case when dismissal = 'bowled' then runs else 0 end) bowled,
sum(case when dismissal = 'run out' then runs else 0 end) run_out,
sum(case when dismissal = 'not out' then runs else 0 end) not_out
from batsman
group by opposition
This sums the runs for each value of dismissal. If you just want to count the occurences, then:
select opposition,
sum(dismissal = 'caught') caught,
sum(dismissal = 'bowled') bowled,
sum(dismissal = 'run out') run_out,
sum(dismissal = 'not out') not_out
from batsman
group by opposition

You can distinctly select all dismissal cases and then pivot dynamically as
SET #sql = NULL;
SELECT GROUP_CONCAT(
CONCAT(
'SUM(CASE WHEN dismissal =''', dismissal, ''' THEN runs ELSE 0 END ) AS ',
dismissal
)
)
INTO #sql
FROM ( SELECT DISTINCT dismissal
FROM Batsman_detail
) b;
SET #sql = CONCAT('SELECT opposition,',#sql,
' FROM Batsman_detail
GROUP BY opposition');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Demo

Related

How to get dynamic columns for a query

I want to create a SQL query which should generate columns according to date range.
For example, if my input parameters for date range in 2013-01-01 and 2013-06-07. Then the query should dynamically create seven month columns between these above two dates. The output should look like this:
data column1 column2 Jan-13 feb-13 mar-13 apr-13 may-13 jun-13 jul-13
I have tried couple of times but not reached to the desired result.
Please share your ideas on this. How should I proceed?
SELECT a.* ,
(SELECT SUM(fid1.TOTAL_AMOUNT) FROM
fact_invoice_details fid1
WHERE DATE_FORMAT(dd.thedate, '%b %y') LIKE '%Jan%' ) AS 'Jan',
(SELECT SUM(fid1.TOTAL_AMOUNT) FROM
fact_invoice_details fid1
where DATE_FORMAT(dd.thedate, '%b %y') LIKE '%Feb%' ) AS 'Feb'
FROM
(
SELECT DISTINCT gl.ACCOUNT_NUMBER,
gl.NAME AS 'GL_Account_Name',
di.account_id 'ACCOUNT_ID',
dpp.company_name 'ACCOUNT_NAME',
dd3.thedate AS 'INVOICE_DATE',
di.OCCURRED_ON_DATE_ID,
di.INVOICE_ID ,
di.INVOICE_NUMBER ,
fid.INVOICE_ITEM_ID,
dpro.sku,
dpro.PRODUCT_NAME,
dci.ISO,
fid.TOTAL_AMOUNT
-- DATE_FORMAT(dd3.thedate, '%b %y') as mon
FROM dim_gl_account gl
/* Some tables also having here */
WHERE dte.TENANT_ID = 155
AND dd3.thedate BETWEEN CAST('2013-01-01' AS DATE) AND CAST('2013-03-01' AS DATE) ) a
as i can not paste the whole code .
What you are attempting to do is pivot the data from rows into columns. Unfortunately MySQL does not have this capability, however, we can use an aggregate function with a CASE statement to get a similar result.
SELECT
cid,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 1 THEN total ELSE 0 END) AS JAN_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 2 THEN total ELSE 0 END) AS FEB_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 3 THEN total ELSE 0 END) AS MAR_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 4 THEN total ELSE 0 END) AS ARR_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 5 THEN total ELSE 0 END) AS MAY_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 6 THEN total ELSE 0 END) AS JUN_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 7 THEN total ELSE 0 END) AS JUL_13
FROM invoices
GROUP BY cid
DEMO
Basically we supplied a CASE line for each month/year that we want to get the SUM for. You could include a line for each month/year and add back the date ranges to your WHERE clause but this would still create columns for all the months with zero values as seen here.
A better way to do this dynamically would be to use a prepared statement. This way we can supply our date range in the WHERE clause and let SQL do the heavy lifting.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN MonthYear = ''',
MonthYear,
''' THEN total ELSE 0 END) AS ',
MonthYear
)
) INTO #sql
FROM (
SELECT
cid,
DATE_FORMAT(duedate, '%b_%y') MonthYear,
total
FROM invoices
WHERE duedate BETWEEN CAST('2013-01-01' AS DATE) AND CAST('2013-07-31' AS DATE)
ORDER BY duedate
) src;
SET #sql = CONCAT("SELECT cid, ", #sql, "
FROM (
SELECT
cid,
DATE_FORMAT(duedate, '%b_%y') MonthYear,
total
FROM invoices
WHERE duedate BETWEEN CAST('2013-01-01' AS DATE) AND CAST('2013-07-31' AS DATE)
ORDER BY duedate
) src
GROUP BY cid");
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DEMO
My examples are obviously much simpler then the snippet you provided but you should be able to incorporate the logic back into your queries. Depending on how you will be making calls you might also consider using a stored procedure as well. Finally, here is a link to an article discussing pivot like functions for MySQL.
Dynamic pivot tables (transform rows to columns)

MySQL sum results into a table grid format

I have the following SQL code:
select distinct members_main.membership_type,
payment_method,sum(amount_paid)
from members_main, members_payments
where members_main.contact_id=members_payments.contact_id
group by members_main.membership_type,payment_method
That returns the following:
I want to be able to return the results in the following table grid format:
Does anyone know if or how I can do this within MySQL?
Thanks,
John
Depending on data complexity either go with bluefeet's way or if the payment_method amount is dynamic then it sounds like you need a pivot table. Try looking at this.
MySQL does not have a pivot function but you can use a CASE expression and an aggregate function to turn the rows into columns:
select m.membership_type,
sum(case when p.payment_method = 'Cash' then amount_paid else 0 end) Cash,
sum(case when p.payment_method = 'Credit Card' then amount_paid else 0 end) CreditCard,
sum(case when p.payment_method = 'Paypal' then amount_paid else 0 end) Paypal
from members_main m
inner join members_payments p
on m.contact_id=p.contact_id
group by m.membership_type;
If you are going to have an unknown number of payment_method's, then you will want to look at using a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN p.payment_method = ',
payment_method,
' THEN amount_paid else 0 END) AS `',
payment_method, '`'
)
) INTO #sql
FROM members_payments ;
SET #sql
= CONCAT('SELECT m.membership_type, ', #sql, '
from members_main m
inner join members_payments p
on m.contact_id=p.contact_id
group by m.membership_type');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Multiple counts and a group by

I have a database (MySQL 5.5) similar to this:
ID Name Page Visited Date
1 Tim Page A 11-2-2000
1 Tim Page B 11-3-2000
1 Tim Page B 11-3-2000
2 Jeff Page C 11-5-2000
2 Jeff Page A 11-11-2000
I want to build a query (trying to at the moment), where the results would be similar to this:
ID Name Page A Visits Page B Visits Page C Visits
1 Tim 1 2 0
I assume that I need to run the following query against a subset (my question is how do I do this with essentially 3 counts)?:
SELECT * From database.mytable GROUP BY ID HAVING COUNT(*) >=1
SELECT ID, Name,
SUM(CASE WHEN `Page Visited` = 'Page A' THEN 1 ELSE 0 END) `Page A Visit`,
SUM(CASE WHEN `Page Visited` = 'Page B' THEN 1 ELSE 0 END) `Page B Visit`,
SUM(CASE WHEN `Page Visited` = 'Page C' THEN 1 ELSE 0 END) `Page C Visit`
FROM tableName
GROUP BY ID, Name
SQLFiddle Demo
if you have unknown number of page, you can also PreparedStatement
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN `Page Visited` =''',
`Page Visited`,
''' then 1 ELSE 0 end) AS ',
CONCAT('`',`Page Visited`, ' Visits`')
)
) INTO #sql
FROM TableName;
SET #sql = CONCAT('SELECT ID, Name, ', #sql, '
FROM tableName
GROUP BY ID, Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo

MYSQL: count column if something if another column is equal to something else

I have the following table:
----------------------------------------------
|ID|Vote_Item_ID|User_ID|Country_code| Vote|
|01| 105102151|user1 | CA| like|
|02| 105102151|user2 | CA|dislike|
|03| 105102151|user3 | UK|dislike|
|04| 105102151|user4 | UK| like|
|05| 105102151|user5 | UK| like|
----------------------------------------------
What I need to do is create an SQL statement that creates an array which totals the likes and dislikes for each country...The script I am using this with has 175 countries, so would this be an inefficient way to about it?
I'm not sure how to go about writing the Select statement, since I want the script to be reusable for many different "vote_item_id"s
I am using PDO with a MYSQL database by the way.
Thanks
SELECT Country_Code,
SUM(CASE WHEN vote = 'like' THEN 1 ELSE 0 END) `like`,
SUM(CASE WHEN vote = 'dislike' THEN 1 ELSE 0 END) dislike
FROM tableName
GROUP BY Country_Code
SQLFiddle Demo
or if you want PreparedStatement (both give the same results)
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN vote = ''',
Vote,
''' then 1 Else 0 end) AS `',
Vote, '`'
)
) INTO #sql
FROM TableName;
SET #sql = CONCAT('SELECT Country_Code, ', #sql, '
FROM tableName
GROUP BY Country_Code');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo
If you had shown your expected output, that would have been great.
Following sample is simple. So let us know what you really need beyond this. Would be happy to pitch in. :)
REFERENCE : SQLFIDDLE
CODE:
SELECT t.country_code,
sum(CASE WHEN t.vote = 'like'
THEN 1 ELSE 0 END) AS LIKES,
sum(CASE WHEN t.vote = 'dislike'
THEN 1 ELSE 0 END) AS DISLIKE
FROM tbl t
GROUP BY t.country_code
;
RESULTS:
COUNTRY_CODE LIKES DISLIKE
CA 1 1
UK 2 1

How to return multiple columns from one table?

How do i return multiple columns from a table ?
My Course table:
| Course | PersId | Taskid | Status |
Computer < > User1 <> 1 < > confirmed
Computer < >User2 <> 1 <> unconfirmed
Computer < >User3 <> 1 <> unconfirmed
Computer < >User1 <> 2 <> confirmed
Computer < >User2 <> 2 <> confirmed
Computer < >User3 <> 2 <> unconfirmed
I want it to return like this:
| PersId | Task_1 | Task_2 |
User1 <> confirmed < > confirmed
User2 <> unconfirmed <> confirmed
User3 <> unconfirmed <> unconfirmed
Question 2: There are other courses(math, english etc) in my table with more tasks than two. Do i need to use some kind of iteration to return the task columns ? because i dont want to make a SQL query for each single course(over 100).
thanks in advance
You can use a CASE and an aggregate:
select persid,
max(case when taskid = 1 then status end) as Task1,
max(case when taskid = 2 then status end) as Task2
from course
group by persid
If you want to include the course info:
select persid,
course,
max(case when taskid = '1' then status end) as Task1,
max(case when taskid = '2' then status end) as Task2
from course
group by persid, course
order by course, persid
See SQL Fiddle with Demo
If you have an unknown number of tasks, then you can use a prepared statement to generate this dynamically:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when taskid = ''',
taskid,
''' then status end) AS Task_',
taskid
)
) INTO #sql
FROM course;
SET #sql = CONCAT('SELECT persid, course, ', #sql, '
FROM course
group by persid, course
order by course, persid');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo
Like this:
SELECT
PersId,
MAX(CASE WHEN Taskid = 1 THEN Status END) AS Task_1,
MAX(CASE WHEN Taskid = 2 THEN Status END) AS Task_2
FROM Courses
GROUP BY persID
Use power of PIVOTE man
PIVOT rotates a table-valued expression by turning the unique values from one column in the expression into multiple columns in the output, and performs aggregations where they are required on any remaining column values that are wanted in the final output.
SELECT Course, PersId, [1] AS [Task_1], [2] AS [Task_2]
FROM
(
SELECT Course, PersId, TaskId, [Status]
FROM t2
) as a
PIVOT
(
MAX([Status])
FOR TaskId IN ([1], [2])
) as b