SQL group by school name - mysql

school_name
class
medium
total
srk
1
english
13
srk
2
english
14
srk
3
english
15
srk
1
french
16
srk
2
french
16
srk
3
french
18
vrk
1
english
17
vrk
1
french
18
I want that output by
school_name
class1eng
class1french
class2eng
class2french
class3english
class3french
[output needed][ otput required
output

You’re looking for multiple select statements along with appropriate cases to satisfy.
This should work for you
Select
school_name,
Sum(Case when (class=1 and medium=‘English’) then total else 0 end) as class1english,
Sum(Case when (class=1 and medium=‘French’) then total else 0 end) as class1french,
Sum(Case when (class=2 and medium=‘English’) then total else 0 end) as class2english,
Sum(Case when (class=2 and medium=‘French’) then total else 0 end) as class2french,
Sum(Case when (class=3 and medium=‘English’) then total else 0 end) as class3english,
Sum(Case when (class=3 and medium=‘French’) then total else 0 end) as class3french
From
table_name
Group by
school_name

Seems to be a simple ask, assumed you also want to order your results. Please check below query if that helps
SELECT school_name, class, medium, SUM(total) AS Total
FROM <Table Name>
GROUP BY school_name, class, medium

This solution is for general purpose, complex, but functional.
I've made it for myself as exercise and challenge.
/* --------------- TABLE --------------- */
CREATE TABLE schools_tab
(school VARCHAR(9), class INT, subj VARCHAR(9), total INT);
INSERT INTO schools_tab VALUES
('srk', 1, 'english', 13),
('srk', 2, 'english', 14),
('srk', 3, 'english', 15),
('srk', 1, 'french', 16),
('srk', 2, 'french', 16),
('srk', 3, 'french', 18),
('vrk', 1, 'english', 17),
('vrk', 1, 'french', 18);
/* -------------- DYNAMIC QUERY --------------- */
SET #sql=NULL;
WITH cte AS (
SELECT school, class, subj, ROW_NUMBER() OVER (PARTITION BY school) AS idx, DENSE_RANK() OVER (ORDER BY school) AS ids
FROM (SELECT DISTINCT school FROM schools_tab) A LEFT JOIN (SELECT DISTINCT class, subj FROM schools_tab) B ON (1=1)
), cte2 AS (
SELECT A.ids, A.idx, A.school, A.class, A.subj, COALESCE(B.total, 0) AS total
FROM cte A LEFT JOIN schools_tab B ON (A.school=B.school AND A.class=B.class AND A.subj=B.subj)
), cte3 AS (
SELECT DISTINCT class, subj
FROM schools_tab
ORDER BY class, subject
)
SELECT CONCAT('WITH RECURSIVE cte AS (
SELECT school, class, subj, ROW_NUMBER() OVER (PARTITION BY school) AS idx, DENSE_RANK() OVER (ORDER BY school) AS ids
FROM (SELECT DISTINCT school FROM schools_tab) A LEFT JOIN (SELECT DISTINCT class, subj FROM schools_tab) B ON (1=1)
), cte2 AS (
SELECT A.ids, A.idx, A.school, A.class, A.subj, COALESCE(B.total, 0) AS total
FROM cte A LEFT JOIN schools_tab B ON (A.school=B.school AND A.class=B.class AND A.subj=B.subj)
), ctx AS ('
'SELECT (SELECT MAX(ids) FROM cte2) AS n,',
GROUP_CONCAT(DISTINCT CONCAT( '(SELECT total FROM cte2 WHERE idx=',idx,' AND ids=n) AS class',class,subj ) ORDER BY class, subj),
' UNION ALL SELECT n-1 AS n,',
GROUP_CONCAT(DISTINCT CONCAT( '(SELECT total FROM cte2 WHERE idx=',idx,' AND ids=n) AS class',class,subj ) ORDER BY class, subj),
' FROM ctx WHERE n>0',
') SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(''srk,vrk'', '','', n+1), '','', -1) AS school,',
GROUP_CONCAT(DISTINCT CONCAT('class',class,subj)),
' FROM ctx ORDER BY school'
) INTO #sql
FROM cte2;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;

Related

How can I get this pivot kind of output in sql

Consider my source table as given below i.e customer.
How can i get the required output as shown using sql (oracle or mysql)
customer :
customer id Purchase_id cashback
123 abc111 5
123 abc112 5
123 abc113 2
345 abc311 0
345 abc312 2
678 abc611 4
678 abc612 3
678 abc613 5
Output Needed:
ID purchare_id_1 purchare_id_2 purchare_id_3 cashback_1 cashback_2 cashback_3
123 abc111 abc112 abc113 5 5 2
345 abc311 abc312 0 2
678 abc611 abc612 abc613 4 3 5
DML and DDL:
create table cust_table (
customer_id int, Purchase_id varchar(100), cashback int
);
insert into cust_table values
(123 , 'abc111' , 5),
(123 , 'abc112' , 5),
(123 , 'abc113' , 2),
( 345 , 'abc311' , 0),
(345 , 'abc312' , 2),
(678 , 'abc611' , 4),
(678 , 'abc612' , 3),
(678 , 'abc613' , 5);
commit;
PS:
Data might be not static, it can change.
WITH
cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY Purchase_id) rn
FROM cust_table )
SELECT customer_id,
MAX(CASE WHEN rn=1 THEN Purchase_id END) purchase_1,
MAX(CASE WHEN rn=2 THEN Purchase_id END) purchase_2,
MAX(CASE WHEN rn=3 THEN Purchase_id END) purchase_3,
MAX(CASE WHEN rn=1 THEN cashback END) cashback_1,
MAX(CASE WHEN rn=2 THEN cashback END) cashback_2,
MAX(CASE WHEN rn=3 THEN cashback END) cashback_3
FROM cte
GROUP BY customer_id
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ec2de721d7089a82a5f7ae669ce2d19e
In MySQL: You can use concat along with group by to see partial result like as below
select customer_id, group_concat(`Purchase_id` separator ',') as `Purchase_idX`,
group_concat(`cashback` separator ',') as `cashbackX` from cust_table
group by customer_id;
If you want the exact result run the below query:
select
SUBSTRING_INDEX(AA, ',', 1) as purchare_id_1,
CASE
WHEN LOCATE(',', AA, 2) = 0 THEN NULL
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(AA, ',', 2), ',', -1)
END AS purchare_id_2,
CASE
WHEN LOCATE(',', AA, LOCATE(',', AA, 1)+1) = 0 THEN NULL
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(AA, ',', 3), ',', -1)
END AS cashback_1,
SUBSTRING_INDEX(BB, ',', 1) as purchare_id_1,
CASE
WHEN LOCATE(',', BB, 2) = 0 THEN NULL
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(BB, ',', 2), ',', -1)
END AS cashback_2,
CASE
WHEN LOCATE(',', BB, LOCATE(',', BB, 1)+1) = 0 THEN NULL
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(BB, ',', 3), ',', -1)
END AS cashback_3
from
( select customer_id, group_concat(`Purchase_id` separator ',') as AA, group_concat(`cashback` separator ',') as BB,
group_concat(`cashback` separator ',') as `cashbackX` from cust_table
group by customer_id) as TB

how to make a apriori table with dynamicaly mysql

i have 3 tables like this
create table order_match
(
id int(10) PRIMARY KEY not null,
order_status_id int(10) not null
);
create table order_match_detail
(
id int(10) PRIMARY KEY not null,
order_match_id int(10) not null,
product_id int(10) NOT NULL
);
create table product
(
id int(10) PRIMARY KEY not null,
name varchar(255) not null
);
Insert into order_match (id, order_status_id)
select 1, 6 union all
select 2, 7 union all
select 3, 6 union all
select 4, 6;
Insert into order_match_detail (id, order_match_id, product_id)
select 1, 1, 147 union all
select 2, 2, 148 union all
select 3, 3, 147 union all
select 4, 4, 149 union all
select 5, 4, 147;
Insert into product (id, name)
select 147, 'orange' union all
select 148, 'carrot' union all
select 149, 'Apple';
with order_match.id = order_match_detail.order_match_id
and order_match_detail.product_id = product.id
i want to make the data where order_status_id not in 7 then it's success transaction and from that success transaction, if the transaction buy apple, then the column of apple contain 1 else if not buying, then 0 and this is my expected results, i want to make this data to analyze in
id (in order_match) | Orange | Carrot | Apple
1 1 0 0
3 1 0 0
4 1 0 1
with that problem i can solve it with this query
select om.id,
count(DISTINCT case when omd.product_id = 147 THEN 1 END) Orange,
count(DISTINCT case when omd.product_id = 148 THEN 1 END) Carrot,
count(DISTINCT case when omd.product_id = 149 THEN 1 END) Apple
from order_match om
left join order_match_detail omd
on om.id = omd.order_match_id
where om.order_status_id in (4, 5, 6, 8)
group by om.id
the real problem is, in my real database, it's contain 1550 product_id, how to make it automatically so no need to input manual the product_id untill 1550 product_id
this is the fiddle https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=c0eb7fe1b012ab1c909d37e325a8d434
i've tried the new queries like this and it still wrong
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'count(case when product.name = ''',
product.name,
''' then 1 end) AS ',
replace(product.name, ' ', '')
)
) INTO #sql
from product;
SET #sql = CONCAT('SELECT omd.order_match_id, ', #sql, ' from order_match_detail omd
left join order_match om
on omd.order_match_id = om.id
left join product p
on omd.product_id = p.id
where om.order_status_id in (4, 5, 6, 8)
group by omd.order_match_id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
your query is almost correct you were missing on alias instead of p you should use 'product' in second query, it would work
SET group_concat_max_len = 1000000;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'count(case when product.name = ''',
product.name,
''' then 1 end) AS ',
'"',replace(product.name, ' ', ''),'"'
)
) INTO #sql
from product;
SET #sql = CONCAT('SELECT omd.order_match_id as id, ', #sql, ' from order_match_detail omd
left join order_match om
on omd.order_match_id = om.id
left join product product
on omd.product_id = product.id
where om.order_status_id in (4, 5, 6, 8)
group by omd.order_match_id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
You can remove DISTINCT from GROUP_CONCAT(DISTINCT if you are sure that product id's is having unique names for each id's
Then i would get the exact result as you mentioned in question else just you will see, Apple column first and Orange column last, overall result is as expected.

MySQL query to do a SELF join and perform aggregation

I have a requirement to get the data from mysql db.
below is the DDL, DML ,sample output and query.
I need help on optimizing the query, because I am sure the query I have wrote is very basic.
create table team (
team_id int,
team_name char(10)
);
create table matches (
matches_id int,
host_team_id int,
guest_team_id int,
host_goals int,
guest_goals int
);
insert into team values (10, 'mumbai');
insert into team values (20, 'delhi');
insert into team values (30, 'banglore');
insert into team values (40, 'chennai');
insert into team values (50, 'gujarat');
insert into matches values (1, 50, 20, 2,1);
insert into matches values (2, 30, 40, 2,0);
insert into matches values (3, 10, 50, 1,1);
insert into matches values (4, 20, 30, 0,1);
insert into matches values (5, 40, 20, 3,2);
insert into matches values (6, 50, 30, 1,0);
insert into matches values (7, 40, 10, 1,2);
-- rules
-- team with more goals wins and gets 3points
-- team which lose gets 0points
-- for a tie, each team gets 1point
expected output [order by goals and then by team_name
team_id | team_name | goals
50 gujarat 7
30 bangalore 6
10 mumbai 4
40 chennai 3
20 delhi 0
QUERY
select * from (select team_id, team_name, sum(goals) as goals from (
select team_id, team_name,
SUM( Case
when host_goals > guest_goals then 3
when host_goals = guest_goals then 1
else 0
end ) as goals
from team t , matches m1
where t.team_id = m1.host_team_id
group by team_id, team_name
union all
select team_id, team_name,
SUM( Case
when guest_goals > host_goals then 3
when host_goals = guest_goals then 1
else 0
end ) as goals
from team t , matches m1
where t.team_id = m1.guest_team_id
group by team_id, team_name
order by goals desc, team_name asc) as finalOut
group by team_id, team_name ) as t1
order by goals desc, team_name asc
Use better criteria instead of having to union two queries. Forget the union all and do this:
select team_id, team_name,
SUM(
(t.team_id = m1.host_team_id) * ((host_goals >= guest_goals) + 2 * (host_goals > guest_goals)) +
(t.team_id = m1.guest__team_id) * ((host_goals <= guest_goals) + 2 * (host_goals < guest_goals))
) as goals
from team t , matches m1
where ((t.team_id = m1.host_team_id) or (t.team_id = m1.guest_team_id))
group by team_id, team_name
This should be a good starting point. (untested)
You should perform the sum, group by and order by only on the most external select
select * from (select team_id, team_name, sum(goals) as goals from (
select
team_id
, team_name
, Case
when host_goals > guest_goals then 3
when host_goals = guest_goals then 1
else 0
end as goals
from team t
Inner join matches m1 on t.team_id = m1.host_team_id
union all
select
team_id
, team_name
, Case
when guest_goals > host_goals then 3
when host_goals = guest_goals then 1
else 0
end
from team t
inner join matches m1 on t.team_id = m1.guest_team_id
) as finalOut
group by team_id, team_name
order by goals desc, team_name asc
I would go for radical simplification:
select t.team_id, t.team_name, sum(points) as points
from teams t join
((select host_team_id as team_id, host_goals as goals, 'host' as which,
(case when host_goals > guest_goals then 3
when host_goals = guest_goals then 1
else 0
end) as points
from matches
) union all
(select guest_team_id as team_id, guest_goals as goals, 'guest' as which
(case when guest_goals > host_goals then 3
when guest_goals = host_goals then 1
else 0
end) as points
from matches
)
) hg
on hg.team_id = t.team_id
group by t.team_id, t.team_name
order by sum(points) desc, team_name;
If you want all teams -- even those who have played no matches -- then use left join.

SQL query - credit , debit , balance

DISCLAIMER : I Know this has been asked numerous times, but all I want is an alternative.
The table is as below :
create table
Account
(Name varchar(20),
TType varchar(5),
Amount int);
insert into Account Values
('abc' ,'c', 500),
('abc', 'c', 700),
('abc', 'd', 100),
('abc', 'd', 200),
('ab' ,'c', 300),
('ab', 'c', 700),
('ab', 'd', 200),
('ab', 'd', 200);
Expected result is simple:
Name Balance
------ -----------
ab 600
abc 900
The query that worked is :
select Name, sum(case TType when 'c' then Amount
when 'd' then Amount * -1 end) as balance
from Account a1
group by Name.
All I want is, is there any query sans the 'case' statement (like subquery or self join ) for the same result?
Sure. You can use a second query with a where clause and a union all:
select name
, sum(Amount) balance
from Account a1
where TType when 'c'
group
by Name
union
all
select name
, sum(Amount * -1) balance
from Account a1
where TType when 'd'
group
by Name
Or this, using a join with an inline view:
select name
, sum(Amount * o.mult) balance
from Account a1
join ( select 'c' cd
, 1 mult
from dual
union all
select 'd'
, -1
from dual
) o
on o.cd = a1.TType
group
by Name
To be honest, I would suggest to use case...
Use the ASCII code of the char and try to go from there. It is 100 for 'd' and 99 for 'c'. Untested example:
select Name, sum((ASCII(TType) - 100) * Amount * (-1)) + sum((ASCII(TType) - 99) * Amount * (-1)))) as balance from Account a1 group by Name.
I would not recommend using this method but it is a way of achieving what you want.
select t.Name, sum(t.cr) - sum(t.dr) as balance from (select Name, case TType when 'c' then sum(Amount) else 0 end as cr, case TType when 'd' then sum(Amount) else 0 end as dr from Account group by Name, TType) t group by t.Name;
This will surely help you!!
The following worked for me on Microsoft SQL server. It has the Brought Forward balance as well
WITH tempDebitCredit AS (
Select 0 As Details_ID, null As Creation_Date, null As Reference_ID, 'Brought
Forward' As Transaction_Kind, null As Amount_Debit, null As Amount_Credit,
isNull(Sum(Amount_Debit - Amount_Credit), 0) 'diff'
From _YourTable_Name
where Account_ID = #Account_ID
And Creation_Date < #Query_Start_Date
Union All
SELECT a.Details_ID, a.Creation_Date, a.Reference_ID, a.Transaction_Kind,
a.Amount_Debit, a.Amount_Credit, a.Amount_Debit - a.Amount_Credit 'diff'
FROM _YourTable_Name a
where Account_ID = #Account_ID
And Creation_Date >= #Query_Start_Date And Creation_Date <= #Query_End_Date
)
SELECT a.Details_ID, a.Creation_Date, a.Reference_ID, a.Transaction_Kind,
a.Amount_Debit, a.Amount_Credit, SUM(b.diff) 'Balance'
FROM tempDebitCredit a, tempDebitCredit b
WHERE b.Details_ID <= a.Details_ID
GROUP BY a.Details_ID, a.Creation_Date, a.Reference_ID, a.Transaction_Kind,
a.Amount_Debit, a.Amount_Credit
Order By a.Details_ID Desc

Columns to Rows queries

I have a table that look like this:
Company Year Revenue Cost Profit
ABC 1 10 6 4
ABC 2 12 7 5
ABC 3 14 8 6
XYZ 1 25 18 7
XYZ 2 27 19 8
XYZ 3 29 20 9
I want it look like this:
Company Item 1 2 3
ABC Revenue 10 12 14
ABC Cost 6 7 8
ABC Profit 4 5 6
XYZ Revenue 25 27 29
XYZ Cost 18 19 20
XYZ Profit 7 8 9
A crosstab query only allows one value. I can do it using separate crosstab queries for Revenue, Cost And Profit and the combine using the Union function but there must be an easier way.
Any help would be really appreciated.
Max
Variation for you to try.
SELECT Company,
item,
SUBSTRING_INDEX(SUBSTRING_INDEX(item_details, ',', 1), ',', -1) AS `1`,
SUBSTRING_INDEX(SUBSTRING_INDEX(item_details, ',', 2), ',', -1) AS `2`,
SUBSTRING_INDEX(SUBSTRING_INDEX(item_details, ',', 3), ',', -1) AS `3`
FROM
(
SELECT Company, 'Revenue' AS item, GROUP_CONCAT(Revenue ORDER BY `Year`) AS item_details
FROM SomeTable
GROUP BY Company
UNION
SELECT Company, 'Cost' AS item, GROUP_CONCAT(Cost ORDER BY `Year`)
FROM SomeTable
GROUP BY Company
UNION
SELECT Company, 'Profit' AS item, GROUP_CONCAT(Profit ORDER BY `Year`)
FROM SomeTable
GROUP BY Company
) Sub1
ORDER BY Company, FIELD(Item, 'Revenue', 'Cost', 'Profit')
SQL fiddle for you:-
http://www.sqlfiddle.com/#!2/995a0/6
For reasons of scalability (and flexibility), problems like this are best left to the application level code (e.g. a simple PHP loop on a well-ordered result set) but, just for fun...
SELECT company
, item
, MAX(CASE WHEN year = 1 THEN value END) y1
, MAX(CASE WHEN year = 2 THEN value END) y2
, MAX(CASE WHEN year = 3 THEN value END) y3
FROM
( SELECT company, year, 'revenue' item, revenue value FROM my_table
UNION
SELECT company, year, 'cost',cost FROM my_table
UNION
SELECT company, year, 'profit',profit FROM my_table
) x
GROUP
BY company
, item
ORDER
BY company
, FIELD(item,'Revenue','Cost','Profit');
Try this:
SELECT Company, Item, Col1 AS 1, Col2 AS 2, Col3 AS 3
FROM (SELECT a.Company, 'Revenue' AS Item, MAX(IF(a.Year = 1, a.Revenue, 0)) AS Col1,
MAX(IF(a.Year = 2, a.Revenue, 0)) AS Col2, MAX(IF(a.Year = 3, a.Revenue, 0)) AS Col3
FROM tableA a
GROUP BY a.Company
UNION
SELECT a.Company, 'Cost' AS Item, MAX(IF(a.Year = 1, a.Cost, 0)) AS Col1,
MAX(IF(a.Year = 2, a.Cost, 0)) AS Col2, MAX(IF(a.Year = 3, a.Cost, 0)) AS Col3
FROM tableA a
GROUP BY a.Company
UNION
SELECT a.Company, 'Profit' AS Item, MAX(IF(a.Year = 1, a.Profit, 0)) AS Col1,
MAX(IF(a.Year = 2, a.Profit, 0)) AS Col2, MAX(IF(a.Year = 3, a.Profit, 0)) AS Col3
FROM tableA a
GROUP BY a.Company
) AS A
ORDER BY Company, FIELD(Item, 'Revenue', 'Cost', 'Profit')
A second way of doing it. Not tested (so probably some typos), but doing some SQL to get the data then looping around the details, lobbing them to an object to put out the rows. This will cope with any where a company is missing a data for a year.
Note that you could greatly simplify the SQL if you had a table of years that you are interested in and a table of companies.
<?php
$sql = "SELECT Sub1.Year, Sub2.Company, IFNULL(SomeTable.Revenue, 0) AS aValue, 'Revenue' AS Item
FROM
(
SELECT DISTINCT Year FROM SomeTable
) Sub1
CROSS JOIN
(
SELECT DISTINCT Company FROM SomeTable
) Sub2
LEFT OUTER JOIN SomeTable
ON Sub1.Year = SomeTable.Year
AND Sub2.Company = SomeTable.Company
UNION
SELECT Sub1.Year, Sub2.Company, IFNULL(SomeTable.Cost, 0) AS aValue, 'Cost' AS Item
FROM
(
SELECT DISTINCT Year FROM SomeTable
) Sub1
CROSS JOIN
(
SELECT DISTINCT Company FROM SomeTable
) Sub2
LEFT OUTER JOIN SomeTable
ON Sub1.Year = SomeTable.Year
AND Sub2.Company = SomeTable.Company
UNION
SELECT Sub1.Year, Sub2.Company, IFNULL(SomeTable.Profit, 0) AS aValue, 'Profit' AS Item
FROM
(
SELECT DISTINCT Year FROM SomeTable
) Sub1
CROSS JOIN
(
SELECT DISTINCT Company FROM SomeTable
) Sub2
LEFT OUTER JOIN SomeTable
ON Sub1.Year = SomeTable.Year
AND Sub2.Company = SomeTable.Company
ORDER BY Company, FIELD(Item, 'Revenue', 'Cost', 'Profit'), Year";
$query = $db->query($sql) or die($db->error()) ;
if ($row = $this->db->fetchAssoc())
{
echo "<table>";
$PrevCompany = $row['Company'];
$PrevItem = $row['Item'];
$aLine = new ProcessLine($PrevCompany, $PrevItem, true);
do
{
if ($PrevCompany != $row['Company'] or $PrevItem != $row['Item'])
{
unset($aLine);
$PrevCompany = $row['Company'];
$PrevItem = $row['Item'];
$aLine = new ProcessLine($PrevCompany, $PrevItem);
}
$aLine->Assign_Detail($row['Year'], $row['aValue'])
} while($row = $this->db->fetchAssoc());
unset($aLine);
echo "</table>";
}
class ProcessLine
{
private $Company;
private $Item;
private $row_details = array();
private $FirstRow = false
public __CONSTRUCT($Company, $Item, $FirstRow=false)
{
$this->Company = $Company;
$this->Item = $Item;
}
public __DESTRUCT()
{
if ($this->Firstrow)
{
echo "<tr><th>".$this->Company."</th><th>".$this->Item."</th>";
foreach($row_details AS $row_year=>$row_value)
{
echo "<th>$row_year</th>";
}
echo "</tr>";
}
echo "<tr><td>".$this->Company."</td><td>".$this->Item."</td><td>".implode("</td><td>", $row_details)."</td></tr>";
}
public Assign_Detail($in_year, $in_value)
{
$row_details[$in_year] = $in_value;
}
}
?>