Get the student result from status when grouping in mysql - mysql

I have created a temporary table from records which looks like below. I want to group student using student id (stu_D). While grouping student result status will be PASSED if he passed all the subject and FAILED if he failed at least one.thanks in advanced

You can try below query -
SELECT stu_D
,stuName
,CASE
WHEN T2.CNT = 0
THEN 'passed'
ELSE 'failed'
END status
FROM (SELECT stu_D
,stuName
,COUNT(CASE
WHEN result = 'FAILED'
THEN 1
END) CNT
FROM T
GROUP BY stu_D
,stuname) T2

SELECT
T.stu_D, T.stuName, IF(T.number_of_failures>0, 'FAILED', 'PASSED') final_result
FROM
(SELECT
stu_D, stuName, COUNT(IF(result='FAILED', 1, NULL)) number_of_failures
FROM your_table
GROUP BY stu_D, stuName) T;

try this query
SELECT stu_Name,result
FROM `marks`
WHERE stu_D not in
(SELECT stu_D FROM marks WHERE result='FAILED')
GROUP BY stu_D

Related

get value of columns in previous row and add to the next columns of the next row

I will create a graph of population by gender every year, and the graph looks like the image below.
But I'm having a hard time with the query.
Query
SELECT
year_added,
COUNT(case when gender='Male' then 1 end) as malecount,
COUNT(case when gender='Female' then 1 end) as femalecount,
COUNT(*) as totalcount
FROM tbl
WHERE status = 1
GROUP BY year_added
Result
In the result, 2016 male count is 4 and female count is 8. In 2017, I want the male count of 2016 to be added on the male count on 2017, meaning 2017 male count will be 5, same with female count and total count. I provided an image below of what the result should look like. Can you help me how to do this for me to proceed on doing the graph? Or is there any other way to achieve this?
Try this:
SELECT
year_added,
#malecount_v := #malecount_v + malecount as malecount,
#femalecount_v := #femalecount_v + femalecount as femalecount,
#totalcount_v := #totalcount_v + totalcount as totalcount
FROM (
SELECT
year_added,
COUNT(case when gender='Male' then 1 end) as malecount,
COUNT(case when gender='Female' then 1 end) as femalecount,
COUNT(*) as totalcount
FROM tbl
WHERE status = 1
GROUP BY year_added
ORDER BY year_added
) t1
CROSS JOIN (SELECT #malecount_v := 0, #femalecount_v := 0, #totalcount_v := 0) t2
In Mysql you can do it with variables, like:
SELECT
year_added,
(#iMalecount := (COUNT(CASE WHEN gender = 'Male' THEN 1 END) + #iMalecount)) AS malecount,
(#iFemalecount := (COUNT(CASE WHEN gender = 'Female' THEN 1 END) + #iFemalecount)) AS femalecount,
(#iTotalcount := (COUNT(gender) + #iTotalcount)) AS totalcount
FROM tbl
WHERE status = 1
GROUP BY year_added
but is not 100% fiable as you can read in the documentation.
In other SQL flavour probably you need a stored procedure.
you can simply use
WITH TableCount AS
(
SELECT
year_added,
COUNT(case when gender='Male' then 1 end) as malecount,
COUNT(case when gender='Female' then 1 end) as femalecount,
COUNT(*) as totalcount
FROM tbl
WHERE status = 1
GROUP BY year_added
)
And after that use following query
SELECT
SUM(malecount) as 'malecount',
SUM(femalecount) as 'femalecount',
SUM(totalcount) as 'totalcount'
FROM TableCount
If you are using MySql you can use temporary table to do something like CTE
CREATE TEMPORARY TABLE IF NOT EXISTS TableCount AS (
SELECT
year_added,
COUNT(case when gender='Male' then 1 end) as malecount,
COUNT(case when gender='Female' then 1 end) as femalecount,
COUNT(*) as totalcount
FROM tbl
WHERE status = 1
GROUP BY year_added
)
And then you can use above query
SELECT
SUM(malecount) as 'malecount',
SUM(femalecount) as 'femalecount',
SUM(totalcount) as 'totalcount'
FROM TableCount
You can use the TEMPORARY keyword when creating a table. A TEMPORARY
table is visible only to the current session, and is dropped
automatically when the session is closed. This means that two
different sessions can use the same temporary table name without
conflicting with each other or with an existing non-TEMPORARY table of
the same name. (The existing table is hidden until the temporary table
is dropped.) To create temporary tables, you must have the CREATE
TEMPORARY TABLES privilege.
By using temporary table concept you can achieve common table expression kind of functionality in MySql

CASE Statement instead of inner join

This is my table structure:
CUST_ID ORDER_MONTH
---------------------
1 1
1 5
2 3
2 4
My objective is to tag these customers as either New or Returning customers.
When I filter the query lets say for month 1 then customer 1 should have the tag 'New' but when I filter it for month 5 then customer 1 should show up as 'Return' as he already made a purchase in month 1.
Same way customer ID 2 should show up as New for month 3 and return for month 4.
I want to do this using a CASE statement and not inner join.
Thanks
If you insist on using a case statement, the logic would be something like "If this is the first month for that user, write new, otherwise write returning." The query would be as follows:
SELECT CASE
WHEN m.month = (SELECT MIN(month) FROM myTable WHERE customer = m.customer) THEN 'New'
ELSE 'Returning' END AS customerType
FROM myTable m;
However, I think this would be nicer and more readable in a JOIN. You can write an aggregation query to get the earliest month for each user, and then use COALESCE() to replace null values with 'Returning'. The aggregation:
SELECT customer, MIN(month) AS minMonth, 'New' AS customerType
FROM myTable
GROUP BY customer ;
To get the rest:
SELECT m.customer, m.month, COALESCE(t.customerType, 'Returning') AS customerType
FROM myTable m
LEFT JOIN(
SELECT customer, MIN(month) AS minMonth, 'New' AS customerType
FROM myTable
GROUP BY customer) t ON t.customer = m.customer AND t.minMonth = m.month;
Here is an SQL Fiddle example that shows both examples.
You don't need a JOIN and a case statement would probably be overkill...
SELECT CUST_ID, IF(COUNT(1)>1, 'Returning', 'New') AS blah
FROM the_table
WHERE ORDER_MONTH <= the_month
GROUP BY CUST_ID
;
Of course, using just month is going to cause problems after a year (or really, after passing December.)
This would be better
SELECT CUST_ID, IF(COUNT(1)>1, 'Returning', 'New') AS blah
FROM the_table
WHERE order_date <= some_date
GROUP BY CUST_ID
;
Well I do not reccomend this way but this is what you want.
select *
,case when order_month = (select MIN(order_month) from #temp t2 where t1.cust_ID =t2.cust_id) THEN 'NEW' ELSE 'Return' end 'Type'
from #temp t1
I think I get what you're trying to do. Your case statement basically just needs to check if the customer's month equals the month you're filtering by. Something like this:
SELECT
<your other fields>,
CASE WHEN Order_Month = <your filter> THEN 'New'
ELSE 'Return'
END AS 'SomeName'
FROM <your table>
Try this query
select a.CUST_ID, a.ORDER_MONTH ,case when b is not null then 'Return' else 'New' end as type
from tablename a
join tablename b on a.CUST_ID=b.CUST_ID and a.ORDER_MONTH>b.ORDER_MONTH
SELECT *,
CASE
WHEN EXISTS (SELECT *
FROM [YourTable] t2
WHERE t1.cust_id = t2.cust_id
AND t2.order_month < t1.order_month) THEN 'Return'
ELSE 'New'
END
FROM [YourTable] t1
This query uses CASE on an EXISTS clause.
The EXISTS is on a subquery which queries the same table for any rows in previous months.
If there are rows for previous months then the EXISTS is true and the CASE returns 'Return'. If there are no rows for previous months then the EXISTS is false and the CASE returns 'New'.

multiple select on stored procedure

I'm trying to do multiple selects from one table but it only shown the last select statement.
CREATE PROCEDURE `usp_GetStockCard` (IN Matecode varchar(10))
BEGIN
(select tran_date as tran_date
from TM_matbalance
where Mate_code=Matecode);
(select Mate_code as Mate_code
from TM_matbalance
where Mate_code=Matecode);
(select tran_qtyx as Qty_in
from TM_matbalance
where tran_type='IN'
and mate_code=matecode);
(select tran_qtyx as Qty_out
from TM_matbalance
where tran_type='OUT'
and mate_code=matecode);
END
I've tried to change semicolon to comma after each select statement but it said that syntax error: missing 'semicolon'.
please help.
I look at your problem and I think I solve it.
Basically there is two problems here first one is to pivot your table where your Tran_Qtyx column become Qty_In and Qty_Out based on value in Tran_Type column (IN or OUT)... That part of problem you solve with this query
SELECT Tran_Date, Mate_Code,
SUM(CASE WHEN Tran_Type = 'IN' THEN Tran_Qtyx ELSE 0 END) Qty_In,
SUM(CASE WHEN Tran_Type = 'OUT' THEN Tran_Qtyx ELSE 0 END) Qty_Out
FROM myTable
WHERE Mate_Code = 'MAT001'
GROUP BY DATE(Tran_Date)
NOTE: In your desired result I only see 'MAT001'as Mate_Code so I stick with that in this solution and exclude MAT002 from result.
More about pivot table you can read here, there you can find a link, which is good to take a look, and where you can find a lot of stuff about mysql query's.
The second part of your problem is to get Qty_Balance column. Similar problem is solved here. It's how to calculate row value based on the value in previous row.
So your complete query could look like this:
SELECT t1.Tran_Date, t1.Mate_Code, t1.Qty_In, t1.Qty_Out,
#b := #b + t1.Qty_In - t1.Qty_Out AS Qty_Balance
FROM
(SELECT #b := 0) AS dummy
CROSS JOIN
(SELECT Tran_Date, Mate_Code,
SUM(CASE WHEN Tran_Type = 'IN' THEN Tran_Qtyx ELSE 0 END) Qty_In,
SUM(CASE WHEN Tran_Type = 'OUT' THEN Tran_Qtyx ELSE 0 END) Qty_Out
FROM myTable
WHERE Mate_Code = 'MAT001'
GROUP BY DATE(Tran_Date)) AS t1
ORDER BY t1.Tran_Date;
NOTE: probably only think you should change here is table name and it's should work.
Here is SQL Fiddle so you can see how that's work!
GL!
You will need to structure your query into one, or pass in a parameter to the stored procedure to select which output/query you want, to restructure your query you will need something like:
`CREATE PROCEDURE `usp_GetStockCard` (IN Matecode varchar(10))
BEGIN
(select tran_date as tran_date, Mate_code as Mate_code, tran_qtyx as Qty
from TM_matbalance
where Mate_code=Matecode
and (tran_type='IN' or tran_type='OUT');
END`
Or try this if you have an ID column:
SELECT coalesce(ta.id, tb.id) as tran_id, coalesce(ta.tran_date, tb.tran_date) as tran_date, coalesce(ta.Mate_code, tb.Mate_code) as Mate_code, ta.tran_type as Qty_In, tb.tran_type as Qty_Out
from (select ta.*
from TM_matbalance ta
where ta.tran_type = 'IN'
and Mate_code=Matecode
) ta full outer join
(select tb.*
from TM_matbalance tb
where tb.tran_type = 'OUT'
and Mate_code=Matecode
) tb
on ta.id = tb.id ;
just replace "id" with the name of your ID column if you don't need to return the id column then remove coalesce(ta.id, tb.id) as tran_id

Getting count of total present and absent in single sql query

Here is my attendance table details Emp_ID(varchar),pdate(datetime),attendance(char(2))
i want to get the total count of attendence days ,total count of absent and total count of present in a single query for a particular date range grouping by emp_id
just an sample example to show you how to proceed with your data
declare #t table (id int,da date,attend Varchar(2))
insert into #t (id,da,attend) values (1,'20141011','P'),
(1,'20141012','A'),
(1,'20141013','P'),
(1,'20141014','A'),
(1,'20141014','P')
select ID,COUNT(da)Total,
(select COUNT(da) from #t where attend = 'A')as Absent,
(select COUNT(da) from #t where attend = 'P')as Present from #t
group by id
this worked for me.
select Emp_ID
,count(case when status ='A' then 1 end) as absent_count
,count(case when status ='P' then 1 end) as present_count
,count(distinct pdate) as Tot_count
from MASTERPROCESSDAILYDATA where pdate between '2014-01-01' and '2014-01-31'
group
by Emp_ID ;
You should try something like that
SELECT Emp_id, present, absent FROM details
NATURAL JOIN(
SELECT COUNT(*) AS present FROM details WHERE Emp_id = table.Emp_id AND attendence='P'
JOIN
SELECT COUNT(*) AS absent FROM details WHERE Emp_id = table.Emp_id AND attendence='A' ) AS ctt
You could use group by, as already demonstrated.
Or you can use window functions/analytic functions.
DECLARE #T TABLE (Emp_id INT,pdate DATE,attendance VARCHAR(2))
INSERT INTO #t (emp_id,pdate,attendance) VALUES (1,'20141011','P'),
(1,'20141012','A'),
(1,'20141013','P'),
(1,'20141014','A'),
(1,'20141014','P')
SELECT
DISTINCT
EMP_ID,
Attendance,
COUNT(*) OVER (PARTITION BY attendance) as CntAttendance
FROM
#t
Try the script below;
select id,
count(distinct pdate) as Total_Attendance_Days,
sum(case when attendance= 'p' then 1 else 0 end) Presents,
sum(case when att = 'a' then 1 else 0 end) Absents
from ##TT
where pdate between '01/04/2015' and '15/04/2015'
group by id
Hope this helps

wrong count in query

I have a table whose structure is as follows:
id int
userid int
status enum ('pending','approved')
dop datetime
Data is as:
id userid status dop
1 24 pending 2011-02-14 06:41:32
2 24 pending 2011-02-15 23:02:45
When I fire the following query:
SELECT count( userid )
FROM t1
WHERE STATUS = 'pending'
GROUP BY userid
It's giving me count as '2', which is wrong, can anyone tell me what's wrong here? and how to get real count as 1
The group by statement is executed after the count. Use this instead:
SELECT count( DISTINCT userid )
FROM t1
WHERE STATUS = 'pending'
Do you want to count the number of user with status pending then?
SELECT count(userid)
FROM t1
WHERE STATUS = 'pending'
GROUP BY status, userid
Try to add the userid in the select clause :
SELECT userid, count( userid )
FROM t1
WHERE STATUS = 'pending'
GROUP BY userid
Maybe adding DISTINCT() on userid?
SELECT count( DISTINCT userid )
FROM t1
WHERE STATUS = 'pending'
GROUP BY userid
You sould use the COUNT(DISTINCT()) construction, it allow you to count the diferent values not NULL (docu)
Try this sentence:
SELECT count( DISTINCT( userid ) )
FROM t1
WHERE STATUS = 'pending'
GROUP BY userid
HTH!