MySql Select data from SubQuery when master query has groupby clause - mysql

I have two tables in MySql db.
1) networks (NetworkId, NetworkType)
2) users (Id, NetworkId, IpAddress)
Using [NetworkId and IpAddress] defines unique users.
Now, I want to use group by clause on NetworkType and at the same time want to list count of all users as like below:
SELECT (SELECT Count(distinct IpAddress) FROM users
WHERE NetworkId in nr.NetworkId ) as UsersCount
FROM networks as nr
GROUP BY NetworkType;
But due to any reason I always gets zero users.
When I run following queries
SELECT GROUP_CONCATE(nr.NetworkId)
FROM networks as nr
GROUP BY NetworkType;
Then I am getting valid values with ',' separated.
Thanks in advance.

Updated per your new information about what you needed with the group by.
How about an upvote for my efforts at least.
Latest SQLFIDDLE
select
mysub.count,
nr.`networkid`
from
`networks` as nr,
(select
count(`ipaddress`) as count,
`networkid`
from
`users`
where
`networkid`
in (
select
`networkid`
from
`networks`
)
group by
`networkid`) as mysub
where nr.`networkid` = mysub.`networkid`
group by nr.`networkid`

Something like this perhaps:
select n.networktype, count(distinct u.ipaddress)
from networks n
join users u on n.networkid = u.networkid
group by n.networktype

Related

SQL: How to merge two complex queries into one, where the second one needs data from the first one

The goal is to load a list of chats where the user sending the request is a member in. Some of the chats are group chats (more than two members) and there I want to show the profile pictures from the users who wrote the last three messages.
The first query to load meta data like the title and the timestamp of the chat is:
SELECT Chat_Users.ID_Chat, Chats.title, Chats.lastMessageAt
FROM Chat_Users
JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) = 2
AND MAX(Chat_Users.ID_User = $userID) > 0
ORDER BY Chats.lastMessageAt DESC
LIMIT 20
The query to load the last three profile pictures from one of the chats loaded with the query above is:
SELECT GROUP_CONCAT(innerTable.profilePictures SEPARATOR ', ') AS 'ppUrls',
innerTable.ID_Chat
FROM
(
SELECT Chat_Users.ID_Chat, Users.profilePictureUrl AS profilePictures
FROM Users
JOIN Chat_Users ON Chat_Users.ID_User = Users.ID
JOIN Chat_Messages ON Chat_Messages.ID_Chat = Chat_Users.ID_Chat
WHERE Chat_Users.ID_Chat = $chatID
ORDER BY Chat_Messages.timestamp DESC
LIMIT 3
) innerTable
GROUP BY innerTable.ID_Chat
Both are working separately but I want to merge them together so I don't have to run the second query in a loop due to performance reasons. Unfortunately I have no idea how this can be achieved because the second query needs the $chatID, which it only gets from the first query.
So to clarify the desired result: The list with the profile picture urls (second query) should be just another column in the result of the first query.
I hope it is explained in a reasonably understandable way. Any help would be much appreciated.
Edit: Sample data from the affected tables:
Table "Chats":
Table "Chat_Users":
Table "Chat_Messages":
Table "Users":
This fufils the brief, however it requires a view because MySQL 5.x doesn't support the WITH clause.
It's long and cluncky and I've tried to shorten it but this is as good as I can get, hopefully someone will pop up in the comments with a way to make it shorter!
The view:
CREATE VIEW last_interaction AS
SELECT
id_chat,
id_user,
MAX(timestamp) AS timestamp
FROM chat_messages
GROUP BY id_user, id_chat
The query:
SELECT
Chat_Users.ID_Chat,
Chats.title,
Chats.lastMessageAt,
urls.pps AS profilePictureUrls
FROM Chat_Users
JOIN Chats ON Chats.ID = Chat_Users.ID_Chat
JOIN (
SELECT
lo.id_chat,
GROUP_CONCAT(users.profilePictureUrl) AS pps
FROM last_interaction lo
JOIN users ON users.id = lo.id_user
WHERE (
SELECT COUNT(*) -- the amount of more recent interactions
FROM last_interaction li
WHERE (li.timestamp = lo.timestamp AND li.id_user > lo.id_user)
) < 3
GROUP BY id_chat
) urls ON urls.id_chat = Chats.id
GROUP BY Chat_Users.ID_Chat
HAVING COUNT(Chat_Users.ID_Chat) > 2
AND MAX(Chat_Users.ID_User = $userID)
ORDER BY Chats.lastMessageAt DESC
LIMIT 20

MySQL Slow union query

I have a query which looks like this:
SELECT address, min(nm_time) as first, max(nm_time) as last
FROM (
(SELECT tx_from as address, nm_time FROM tbl_transactions)
UNION ALL
(SELECT tx_to as address, nm_time FROM tbl_transactions)
) a
GROUP by address
ORDER BY first LIMIT 0,10
This query takes about 5 seconds to complete (on a fast server)
The table tbl_transactions has around 200k records in it. What I'm trying to accomplish is create a list with all unique addresses and their first and last transaction timestamp. I am using pagination, hence the LIMIT 0,10.
Any tips how I could improve this?
I'm not sure if either of these will help, but here are two ideas.
One thing to try is doing aggregation in the subqueries:
SELECT address, min(minnt) as first, max(maxnt) as last
FROM ((SELECT tx_from as address, min(nm_time) as minnt, max(nm_time) as maxnt
FROM tbl_transactions
GROUP BY tx_from
)
UNION ALL
(SELECT tx_to as address, min(nm_time) as minnt, max(nm_time) as maxnt
FROM tbl_transactions
GROUP BY tx_to
)
) a
GROUP by address
ORDER BY first
LIMIT 0, 10;
One method that can be faster than aggregation is to start with a list of addresses and use correlated subqueries to get the information. For this to work, you need a table of addresses. The query would look like:
SELECT a.address,
least((select min(nm_time) from tbl_transactions t where t.tx_from = a.address),
(select min(nm_time) from tbl_transactions t where t.tx_to = a.address)
) as first,
least((select max(nm_time) from tbl_transactions t where t.tx_from = a.address),
(select max(nm_time) from tbl_transactions t where t.tx_to = a.address)
) as last
FROM addresses a
ORDER BY first
LIMIT 10 OFFSET 0;
This needs two indexes for performance: tbl_transactions(tx_from, nm_time) and tbl_transactions(tx_to, nm_time).

How to combine 2 mysql queries

I have the following 2 queries.
Query 1 :
select distinct(thread_id) from records where client_name='MyClient'
Query 2 :
select max(thread_no) from records
where thread_id='loop_result_from_above_query' AND action='Reviewed'
Is it possible to combine them into a single query ?
The second query is run on every result of the first query.
Thank you.
See attached image of a small snippet of mysql records.
I need a single mysql query to output only records which have action="MyAction" as the latest records for a given set of thread_ids. In the sample data set : record with Sr: 7201
I hope this helps in helping me :)
SELECT client_name, thread_id, MAX(thread_no) max_thread
FROM records
WHERE action='Reviewed' AND client_name='MyClient'
GROUP BY client_name, thread_id
UPDATE 1
SELECT a.*
FROM records a
INNER JOIN
(
SELECT thread_id, max(sr) max_sr
FROM records
GROUP BY thread_id
) b ON a.thread_id = b.thread_id AND
a.sr = b.max_sr
WHERE a.action = 'MyAction'
You can use SELF JOIN, but it is not advisable and will impact your query performance. Please check below query for your reference
SELECT DISTINCT r1.thread_id, MAX(r2.thread_no) from records r1 LEFT JOIN records r2 ON r2.thread_id=r1.thread_id WHERE r1.client_name='MyClient' AND r2.action='Reviewed'
SELECT a.maxthreadid,
b.maxthreadno
FROM (SELECT DISTINCT( thread_id ) AS MaxThreadId
FROM records
WHERE client_name = 'MyClient') a
CROSS JOIN (SELECT Max(thread_no) AS MaxThreadNo
FROM records
WHERE thread_id = 'loop_result_from_above_query'
AND action = 'Reviewed') b
Try this.
SELECT *
FROM (SELECT Row_number()
OVER (
partition BY thread_id
ORDER BY thread_no) no,
Max(thread_no)
OVER(
partition BY thread_id ) Maxthread_no,
thread_id,
action,
client_name
FROM records
Where client_name = 'MyClient') AS T1
WHERE no = 1
AND action = 'Reviewed'

SQL to select max record that is not in the future

My table structure looks like this
tblclients
clientid
clientname
tblfacilities
facilityid
facilityname
tblclientfacilities
clientid
facilityid
moveindate
I allow my users to enter a future date as a move in date, what I want to do is select the current facility that my client resides in. I've tried
SELECT
clientid,
facilityid,
moveindate
FROM
tblclientfacilities
WHERE
MAX(tblclientfacilities.moveindate) <= now()
GROUP BY
tblclientfacilities.clientid
Obviously that's not working. Could someone get me on the right track here? Thanks!
SELECT clientId,
(
SELECT facilityId
FROM tblclientfacilities cf
WHERE cf.clientId = c.clientId
AND cf.moveDate <= NOW()
ORDER BY
cf.clientId DESC, moveDate DESC
LIMIT 1
) AS currentFacility
FROM tblclients c
Create a unique index on tblclientfacilities (clientId, moveDate, facilityId) for this to work fast.
Try using HAVING for aggregate functions.
HAVING MAX(tblclientfacilities.moveindate) <= now()
If you use the group by clause consider using the having clause if you want to filter on the aggregated results
SELECT
clientid,
moveindate,
facilityid
FROM
tblclientfacilities
GROUP BY
tblclientfacilities.clientid
HAVING MAX(tblclientfacilities.moveindate) <= now()
Doc: http://www.w3schools.com/sql/sql_having.asp
You are confusing the usage of where with the usage of having.
A quick reference:
SELECT # Desired columns and expressions
FROM # Source tables, and possible joins
WHERE # Filter conditions on the raw data
GROUP BY # Fields you want your data to be grouped-by
HAVING # Filter conditions on the grouped data
Join your client/facility/date records against the records of each client's most recent move-in:
SELECT tcf.*
FROM tblclientfacilities tcf
INNER JOIN ( SELECT clientid, MAX(moveindate) AS moveindate
FROM tblclientfacilities
WHERE moveindate < CURRENT_DATE
GROUP BY 1, 2) most_recent_moveins
ON tcf.clientid = most_recent_moveins.clientid
AND
tcf.moveindate = most_recent_moveins.moveindate)

How can I pull out only records that have FAIL against them?

I'm sure this will be quite simple for some one clued up in SQL but I think it needs a sub query or something. I have a table which basically has a load of order numbers in it and a reply column from an XML API. Either FAIL or SUCCESS.
A brand new row is inserted into the DB after every request. So there may be 5 FAILS for one order number, and on the 6th attempt a record is inserted saying SUCCESS.
How can I put out order numbers that ONLY have a FAIL status next to them?
This will allow me to figure out what records need looking into that continuously fail in the API request.
Try this, by grouping your orders with primary key (order_id)
SELECT * FROM
(
SELECT GROUP_CONCAT(status) as status_combined, order_id
FROM orders
GROUP BY order_id
) AS order_tmp
WHERE status_combined NOT LIKE '%SUCCESS%'
Edit (As per asker comments)
SELECT * FROM
(
SELECT GROUP_CONCAT(status) as status_combined, order_id
FROM orders
JOIN certificates ON certificates.Ordernumber = orders.OrderNumber
GROUP BY order_id
) AS order_tmp
WHERE status_combined NOT LIKE '%SUCCESS%'
please make sure you need to join based on "Ordernumber" or "order_id"
Try this
select m.*
from Main m
join Transactiontable tt
on m.orderid = tt.orderid
group by tt.status , m.orderid
having count(case when tt.status = "failed") = count(tt.status)
You can use simple sql query using a where clause:
select *
from some_table
where Column_From_some_table_has_value = your_particular_value
thats enough.
You can have a look at How to use where clause in sql
Thanks
This is probably the cleanest way to do it:
select *
from mytable
where id in (
select id
from mytable
group by id
having sum(status = 'SUCCESS') = 0)
I'm not a fan of #Minesh's answer because it uses both an aggregate function and the LIKE operator. Both of those can cause performance issues since there won't be any indexes to help the query out with the difficult part of the work. The LIKE clause particularly is a lot of work for the database since it will need to scan every result.
I'm more familiar with SQL Server, but this should work well for you:
SELECT *
FROM Orders
WHERE OrderNumber NOT IN (
SELECT OrderNumber
FROM Orders
WHERE Status = 'SUCCESS')
AND OrderNumber NOT IN (
SELECT OrderNumber
FROM Certificates
WHERE OrderStatus = 'CANCELLED')