SQL update table attribute after table join - mysql

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.

Related

MySQL - Display null column from child table if all values are not distinct

I have the following tables, for example:
invoices
ID Name
1 A
2 B
3 C
4 D
5 E
transactions
ID Invoice_ID User_ID
1 1 10
2 1 10
3 1 10
4 2 30
5 3 20
6 3 40
7 2 30
8 2 30
9 4 40
10 3 50
Now I want to make a select that will pull the invoices and the user_id from the related transactions, but of course if I do that I won't get all the ids, since they may be distinct but there will be only one column for that. What I want to do is that if there are distinct User_ids, I will display a pre-defined text in the column instead of the actual result.
select invoices.id, invoices.name, transactions.user_id(if there are distinct user_ids -> return null)
from invoices
left join transactions on invoices.id = transactions.invoice_id
and then this would be the result
ID Name User_ID
1 A 10
2 B 30
3 C null
4 D 40
5 E null
Is this possible?
You can do the following :
select
invoices.id,
invoices.name,
IF (
(SELECT COUNT(DISTINCT user_id) FROM transactions WHERE transactions.invoice_id = invoices.id) = 1,
(SELECT MAX(user_id) FROM transactions WHERE transactions.invoice_id = invoices.id),
null
) AS user_id
from invoices
Or, alternatively, you can use the GROUP_CONCAT function to output a comma-separated list of users for each invoice. It is not exactly what you asked, but maybe in fact it will be more useful :
select
invoices.id,
invoices.name,
GROUP_CONCAT(DISTINCT transactions.user_id SEPARATOR ',') AS user_ids
from invoices
left join transactions on invoices.id = transactions.invoice_id
group by invoices.id
Try somethingh like:
select i.id, i.name, t.user_id
from invoices i left join
(
select invoice_ID, User_ID
from transactions
group by invoice_ID
having count(invoice_ID)=1
) t on i.id=t.invoice_id
SQL fiddle
You could list all the transactions that have multiple user ids, like this:
select invoices.id, invoices.name, null
from invoices
left join transactions on invoices.id = transactions.invoice_id having count(distinct transactions.user_id) > 1
Also, I think this CASE might suit your needs here:
select invoices.id, invoices.name,
case when count(distinct transactions.user_id) > 1 then null else transactions.user_id end
from invoices
left join transactions on invoices.id = transactions.invoice_id
group by invoices.id
although, I'm not sure this is syntactically correct

Group By MySQL Query that returns specific results with parameters

Before a user starts a private chat (between 2 members, not a group chat) I want to check and see if there is already a chat consisting of only these two members. In case they've deleted the chat on their end, when they go to message that same user again I want it to merge with the old chat instead of starting a duplicate chat for the same two members.
This is my structure
`chats` table
id created_time
1 [TIMESTAMP]
2 [TIMESTAMP]
`chats.parties` table
id chat_id member_id invited_by
1 1 1 0 // creator of chat
2 1 2 1
3 1 3 1
4 2 1 0
5 2 2 1
Group by chat_id but only return results that contain a row with member_id=1 and member_id=2; no more, no less.
In the case of the tables above, only the chat_id=2 row(s) would be returned because chat_id=1 contains a 3rd member.
Is this possible with raw SQL? I'd prefer to not loop through in php as it would take a while with a lot of chats.
Here are two different ways to get the result you are looking for:
-- using conditional aggregation
select chat_id from chat_parties
group by chat_id
having sum(case when member_id = 1 then 1 else 0 end) > 0
and sum(case when member_id = 2 then 1 else 0 end) > 0
and sum(case when member_id not in (1, 2) then 1 else 0 end) = 0
-- using a correlated subquery
select chat_id from chat_parties c1
where member_id in (1,2)
and not exists (
select 1 from chat_parties where chat_id = c1.chat_id and member_id not in (1,2)
)
group by chat_id having count(distinct member_id) = 2
Change the table names to fit your actual setup.
Using conditional COUNT
SQL Fiddle Demo
SELECT c.`id`
FROM chats c
LEFT JOIN chats_parties cp
ON c.`id`= cp.`chat_id`
GROUP BY c.`id`
HAVING COUNT(case when `member_id` = 1 then 1 END) >= 1
AND COUNT(case when `member_id` = 2 then 1 END) >= 1
AND COUNT(DISTINCT `member_id` ) = 2

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.

Select last N rows following a condition

I've got a database table with logs which has 3 columns:
date | status | projectId
status can be either 0 or 1, primary key is on date and projectID
I'm trying to find out how many times a projectID had status 0 since the last time it was 1.
so if there would be only one projectId
date | status | projectId
1 0 3
2 0 3
3 1 3
4 1 3
5 0 3
6 0 3
this should return 2 (row 5 and 6 are 0 and row 4 is 1)
The thing that makes it hard for me is that I have to maintain the order of date. What would be a good way to tackle such problems, and this one in particular?
Here is how you would do it for one project:
select count(*)
from logs l
where status = 0 and
projectid = 3 and
date > (select max(date) from logs where projectid = 3 and status = 1)
Here is how you would do it for all projects:
select l.projectId, count(l1.projectId)
from logs l left outer join
(select projectId, max(date) as maxdate
from logs
where status = 1
group by projectId
) l1
on l.projectId = l1.projectId and
l.date > l1.date and
l.status = 0
group by l.projectId;
here you have an option in just one select.
http://sqlfiddle.com/#!2/6ce87/11
select *
from logs
where status=0 and date > (select date from logs where status=1 order by date desc limit 1)
Here's one way to get the result for all project_id:
SELECT m.project_id
, COUNT(1) AS mycount
FROM ( SELECT l.project_id
, MAX(l.date) AS latest_date
FROM mytable l
WHERE l.status = 1
) m
JOIN mytable t
ON t.project_id = m.project_id
AND t.date > m.latest_date
AND t.status = 0
If you need only a subset of project_id, the predicate should be added to the WHERE clause in the inline view query:
WHERE l.status = 1
AND l.project_id IN (3,5,7)
EDIT
That query does not return a row if there is no status=0 row after the latest status=1 row. To return a zero count, this could be done with an outer join.
SELECT m.project_id
, COUNT(t.status) AS mycount
FROM ( SELECT l.project_id
, MAX(l.date) AS latest_date
FROM mytable l
WHERE l.status = 1
AND l.project_id IN (3)
) m
LEFT
JOIN mytable t
ON t.project_id = m.project_id
AND t.date > m.latest_date
AND t.status = 0
For optimum performance, the statement could make use of an index with leading columns of project_id and date (in that order) and including the status column, e.g.
ON mytable (`project_id`,`date`,`status`)

increase query performance in 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