increase query performance in mysql - mysql

I have used this query
SELECT COUNT(CASE WHEN C <= 1 THEN 1 END) AS nooffamiliesHavingcount1,
COUNT(CASE WHEN C BETWEEN 2 AND 4 THEN 1 END) AS nooffamiliesHavingcountbetween2And4,
COUNT(CASE WHEN C > 4 THEN 1 END) AS nooffamiliesHavingcountgreaterthan3
FROM ( SELECT COUNT(*) AS C
FROM user where user_id = (select user_id from location where location_id in(select location_id from country where state_name='STATE'))
GROUP BY House_No
) t
Here sub query returning approximately 10000 records . The user
table has 10,00,000 records. It is taking too much time.Then the
error it is saying is server gone away. I am using mysql.
I searched from google.But no luck for me.
What changes i need to do for my tables.How i can execute this query successfully by
increasing the query performance.Please suggest me.Thanks in advance....

Try this query
SELECT
COUNT(CASE WHEN C <= 1 THEN 1 END) AS nooffamiliesHavingcount1,
COUNT(CASE WHEN C BETWEEN 2 AND 4 THEN 1 END) AS nooffamiliesHavingcountbetween2And4,
COUNT(CASE WHEN C > 4 THEN 1 END) AS nooffamiliesHavingcountgreaterthan3
FROM
(SELECT
COUNT(*) AS C
FROM
user u,
location l,
country c
where
l.state_name='STATE' AND
l.some_other_column_id= 4 AND <------- Add your condition
c.location_id = l.location_id AND
u.user_id = l.user_id
GROUP BY
u.House_No) t
Use proper joins as it will be easy to understand..
SELECT
COUNT(CASE WHEN C <= 1 THEN 1 END) AS nooffamiliesHavingcount1,
COUNT(CASE WHEN C BETWEEN 2 AND 4 THEN 1 END) AS nooffamiliesHavingcountbetween2And4,
COUNT(CASE WHEN C > 4 THEN 1 END) AS nooffamiliesHavingcountgreaterthan3
FROM
(SELECT
COUNT(*) AS C
FROM
user u
INNER JOIN
location l
ON
l.state_name='STATE' AND
l.some_other_column_id= 4 <------- Add your condition
u.user_id = l.user_id
INNER JOIN
country c
ON
c.location_id = l.location_id
GROUP BY
u.House_No) t
EDITED
In most cases JOINs are faster than sub-queries and it is very rare for a sub-query to be faster.
I accept using subquery is more logical and easy to understand but when it comes about performance it is not as good as joins.
If you are using joins your db will optimize your query on its own which is not in the case of subquery.
Try using explain for both of your query and you will get clear idea how the query executes.
Hope this helps...

Can you try below:
SELECT COUNT(CASE WHEN COUNT() <= 1 THEN 1 END) AS nooffamiliesHavingcount1,
COUNT(CASE WHEN COUNT() BETWEEN 2 AND 4 THEN 1 END) AS nooffamiliesHavingcountbetween2And4,
COUNT(CASE WHEN COUNT(*) > 4 THEN 1 END) AS nooffamiliesHavingcountgreaterthan3
FROM User user
Inner JOIN
(select user_id from location loc
Inner Join country con
on loc.location_id =con.location_id where state_name='STATE' )as temp
on user.user_id =temp.user_id group by House_No

Related

Show count 0 when condition is not met in MySQL group by

Sample Records:
examid setcode answer
------- -------- --------
10 A A
10 A B
10 A X
10 B A
10 B B
10 B C
I am trying to find count group by setcode where answer is X.
I have tried the following query:
SELECT setCode,COUNT(answer) FROM mcq_answer WHERE examid=10 AND answer='X' GROUP BY setCode
This one is returning the following result:
setcode count
------- --------
A 1
But I am looking for the following:
setcode count
------- --------
A 1
B 0
Setcode is dynamic here. I have mentioned A and B only. There may be more setocdes as C,D,E,F etc. How can I do it. I am using MySQL
you can use below query
SELECT C.setCode ,
SUM(CASE WHEN B.setcode IS NULL THEN 0
ELSE 1
END) AS answer
FROM ( SELECT A.setCode ,
COUNT(1) AS cnt
FROM mcq_answer A
GROUP BY A.setCode
) C
LEFT JOIN mcq_answer b ON C.setcode = B.setcode
AND B.answer = 'X'
GROUP BY C.setCode
You can use SUM + IF to have this result:
SELECT `setCode`, SUM(if (`answer`='X', 1,0)) as answer
FROM `mcq_answer`
WHERE examid=10
GROUP BY `setcode`
Try:
SELECT A.setcode,
COUNT(CASE WHEN B.answer ='X' THEN B.answer ELSE NULL END) as count_x
FROM
(SELECT DISTINCT setcode FROM YOUR_TABLE) A
LEFT JOIN
YOUR_TABLE B
ON A.setcode = B.setcode
GROUP BY A.setcode;
It also works for dynamic set of values for setcode.

SQL update table attribute after table join

New to Sql.
I have two tables.
client:
client_id client_name status
1 JZ NULL
2 KD NULL
3 TF NULL
and
transactions:
transaction_id Amount client_id
1 5 1
2 5 2
3 5 3
I can do a join as follows:
SELECT client.status, client.client_id, client.client_name, SUM(transactions.Amount) AS Balance
FROM client
JOIN transactions ON transactions.client_id=client.client_id
GROUP BY client.client_id
ORDER BY client_id
and I get this result:
client_id client_name balance status
1 JZ 5 NULL
2 KD 5 NULL
3 TF 5 NULL
However, I would like to update the value in 'status' to 'ON' if a client balance is >=0, and to 'OFF' if <0. Is it possible to do this so that it updates the 'client' table?
Your query has some syntax errors, but the answer to your question is basically a CASE expression:
SELECT c.client_id, c.client_name, SUM(t.Amount) AS Balance,
(CASE WHEN SUM(t.Amount) < 0 THEN 'OFF' ELSE 'ON' END) as status
FROM client c JOIN
transactions t
ON t.client_id = c.client_id
GROUP BY c.client_id
ORDER BY c.client_id, c.client_name;
If you want to update the value in the table you need an update. One method that should work on most databases is:
update client
set status = (select (case when sum(t.Amount) < 0 then 'OFF' else 'ON' end)
from transactions t
where t.client_id = client.client_id
);
However, I wouldn't recommend doing this. The next transaction could invalidate the statuses. For now, it is probably better to write the query to get the information when you need it.

SQL Select where user1 has more than one and user2 has none

Here's my Query to find id's for userid 2, I want to run a query that finds entries where userid=2 and amount>1 AND userid 1 has none of that id
SELECT id, amount FROM collection WHERE userid='2' AND amount>1
I'm not sure how to do an if statement inside a SQL query, but there has to be a way to easily do this.
Any help would be appreciated
If I understand right, you want The list of users having user ID = 2 and amount > 1. This list should ignore the records where ID is not in user ID = 1
Sample Input/Ouput:
ID UserID Amount Returned?
1 2 0 No (Amount 0)
2 2 10 Yes
3 1 10
3 2 5 (No, since ID =3 exists with Userid = 1)
Below Query should help you with it.
SELECT C.ID, C.AMOUNT
FROM COLLECTION C
WHERE C.USERID = 2 AND C.AMOUNT > 1
AND C.ID NOT IN
( SELECT D.ID
FROM COLLECTION D
WHERE D.USERID = 1
);
Fiddle here
"where userid=2 and amount>1 AND userid 1 has none of that id"
You can use NOT EXISTS:
SELECT id, amount
FROM collection c
WHERE c.userid = '2'
AND c.amount > 1
AND NOT EXISTS
(
SELECT 1 FROM collection c2
WHERE c2.userid = '1'
AND c.id = c2.id
)
SELECT id, amount
FROM collection
WHERE userid='2' AND amount > 1 AND
NOT id in (SELECT id FROM collection WHERE userid='1' AND amount > 1)
I'm not sure this is how your database is working, but this is how I would've solved it.

Mysql Nested Query with two relational table

Table reports :
id (int)
catid (int)
pub_date (datetime)
Table categories :
id (int)
parent_id (int)
I want to get the report results with count(id) 's and group by catid's parent_id.
For example :
Date Count(parent_id = 1) Count(parent_id = 2) Count(parent_id = 3)
2014-02-24 2 5 8
We should have get the results in only a line per day.
Thanks too much in advance.
How about using the below query (SQL Fiddle):
SELECT SUM(CASE WHEN r.id = 1 THEN 1 ELSE 0 END) AS PID_1,
SUM(CASE WHEN r.id = 2 THEN 1 ELSE 0 END) AS PID_2,
SUM(CASE WHEN r.id = 3 THEN 1 ELSE 0 END) AS PID_3
FROM reports AS r
INNER JOIN categories AS c ON c.parent_id = r.id
GROUP BY r.pub_date

Slow running query, is there a better way?

I've got a query that produces the right result, its just very slow. I feel like there must be a better way (perhaps without subqueries).
Table, result and query are below. I've anonymized the data and I have 8 subqueries rather than 2, but the format is the same.
Table "a":
id userId type amount
------------------------------------
1 1 a 400
2 1 b 300
3 1 c 230
4 2 a 600
5 2 b 500
6 2 c 430
I've got an index on each column and one additional one that encompasses the userId and type columns. I can also guarantee you that userId and type are unique (i.e. there would't be two type 'a' for user 1).
Desired Result:
userId typeAtotal typeBtotal
--------------------------------
1 400 300
2 600 500
My Query:
SELECT userId,
(SELECT amount
FROM a AS a2
WHERE a2.userId = a1.userId
AND a2.type = 'a') AS aAmt,
(SELECT amount
FROM a AS a3
WHERE a3.userId = a1.userId
AND a3.type = 'b') AS bAmt
FROM a AS a1
WHERE type IN ('a','b')
GROUP BY userId
Use:
SELECT t.userid,
MAX(CASE WHEN t.type = 'a' THEN amount ELSE NULL END) AS typeAtotal,
MAX(CASE WHEN t.type = 'b' THEN amount ELSE NULL END) AS typeBtotal
FROM YOUR_TABLE t
GROUP BY t.userid
If there can be more than one amount for either type - this will return the highest. If you want such situations added, use SUM:
SELECT t.userid,
SUM(CASE WHEN t.type = 'a' THEN amount ELSE NULL END) AS typeAtotal,
SUM(CASE WHEN t.type = 'b' THEN amount ELSE NULL END) AS typeBtotal
FROM YOUR_TABLE t
GROUP BY t.userid
Looks like cross-tabulation to me. You might try something like this:
SELECT userId,
SUM(IF(a.type = 'a'), a.amount, 0) AS aAmount,
SUM(IF(a.type = 'b'), a.amount, 0) AS bAmount
FROM a
WHERE type IN ('a', 'b')
GROUP BY a.userId
You might want to read this rather well-written tutorial: http://dev.mysql.com/tech-resources/articles/wizard/index.html
Edit: fixed the ELSE condition.