SQL join: selecting last records from duplicates - mysql

I am working on python project "Linux Server Monitor". When you load monitoring website you create your servers and table look like this:
Servers:
+-----------+-----------+---------+-------------+
| server_id | auth_code | user_id | server_name |
+-----------+-----------+---------+-------------+
| 0 | xyz123 | 1 | My Server 1 |
| 1 | aabb | 45 | BlaBlaUser |
| 2 | asdfh1 | 1 | My Server 5 |
+-----------+-----------+---------+-------------+
After the server is created, you start python script and it starts to upload monitoring data to this table (where all data is stored, from all users):
Monitoring Data:
+----+-----------+---------+---------+------------+-----------------+------------------+
| id | auth_code | user_id | os_name | os_version | external_ip | some_other_table |
+----+-----------+---------+---------+------------+-----------------+------------------+
| 0 | aabb | 45 | Ubuntu | 16.04 | 77.88.99.100 | dynamic data |
| 1 | aabb | 45 | Ubuntu | 16.04 | 77.88.99.100 | dynamic data 2 |
| 2 | aabb | 45 | Ubuntu | 16.04 | 10.10.10.10 | dynamic data 3 |
| 3 | xyz123 | 1 | Fedora | 27 | 92.94.95.99 | different data 1 |
| 4 | xyz123 | 1 | Fedora | 27 | 92.94.95.99 | different data 2 |
| 5 | xyz123 | 1 | Fedora | 27 | 92.94.95.99 | different data 3 |***
| 6 | asdfh1 | 1 | CentOS | 7 | 5.5.5.5 | some new data 1 |
| 7 | asdfh1 | 1 | CentOS | 7 | 5.5.5.5 | some new data 2 |
| 8 | asdfh1 | 1 | CentOS | 7 | 2.2.2.2 | some new data 3 |###
+----+-----------+---------+---------+------------+-----------------+------------------+
I want to join first and second table to show user "important" information like this:
Your Active Servers:
+-----------+-------------+---------+------------+-----------------+
| server_id | server_name | os_name | os_version | external_ip |
+-----------+-------------+---------+------------+-----------------+
| 0 | My Server 1 | Fedora | 27 | 92.94.95.99 |***
| 2 | My Server 5 | CentOS | 7 | 2.2.2.2 |###
+-----------+-------------+---------+------------+-----------------+
If I join first and second table by user_id it will load duplicates too but I want only last inserted record of that particular user's server. In this case on last record "external_ip" of one server changed but other one remained the same (marked with *** and ###).

you could use an inner join on subselect for the max id for user, server on monitorig
select s.server_id, s.server_name, m.os_name, m.os_version, m.external_ip
from servers s
inner join Monitoring m on m.user_id = s.user_id and s.auth_code = m.auth_code
inner join (
select max(id) max_id, user_id, os_name, os_version
from Monitoring
group by user_id, os_name, os_version
) t on t.max_id = m.id
where user_id = 1

Related

How to do a ORDER BY inside a INNER JOIN to get last status update?

I need to get records of 2 tables using filters and sorting.
Tables: ra_pedidos, ra_pedido_has_pedido_status
It worked perfectly in MySQL5.0, but now in 5.5 it does not bring the correct result anymore.
I need in the same line to bring the date of the first status and last change of status of each request and ignore the canceled ones.
Example search (ra_pedido_has_pedido_status):
SELECT id_pedido, id_pedido_status, data
FROM ra_pedido_has_pedido_status
WHERE id_pedido IN (86291, 86745, 86622);
Result:
+-----------+------------------+---------------------+
| id_pedido | id_pedido_status | data |
+-----------+------------------+---------------------+
| 86291 | 1 | 2017-10-30 11:16:12 |
| 86291 | 2 | 2017-10-30 14:14:53 |
| 86291 | 3 | 2017-10-31 08:18:47 |
| 86291 | 11 | 2017-11-07 12:08:04 |
| 86622 | 1 | 2017-11-04 12:23:21 |
| 86622 | 2 | 2017-11-04 12:47:33 |
| 86622 | 3 | 2017-11-06 08:24:20 |
| 86622 | 90 | 2017-11-07 08:40:55 |
| 86745 | 1 | 2017-11-07 10:59:51 |
| 86745 | 2 | 2017-11-07 11:09:46 |
+-----------+------------------+---------------------+
Now report SQL:
[EDIT] Explain: JOIN get first match row, then the ORDER BY on SELECT inside this join force to get last row. Note that id_pedido 86622 should not list because it is canceled (last id_pedido_status = 90), and this is filtered in HAVING
SELECT o.id_pedido AS id, pus.id_pedido_status, pus.data
FROM ra_pedido AS o
INNER JOIN
(
SELECT ra_pedido_has_pedido_status.*
FROM ra_pedido_has_pedido_status
ORDER BY data DESC
) AS pus ON o.id_pedido = pus.id_pedido
WHERE o.id_pedido IN (86291, 86745, 86622)
GROUP BY o.id_pedido
HAVING id_pedido_status < 90
ORDER BY data DESC;
This is the result using MySQL 5.0, the expected:
+-------+------------------+---------------------+
| id | id_pedido_status | data |
+-------+------------------+---------------------+
| 86291 | 11 | 2017-11-07 12:08:04 |
| 86745 | 2 | 2017-11-07 11:09:46 |
+-------+------------------+---------------------+
And this is the result using MySQL 5.5, not expected:
+-------+------------------+---------------------+
| id | id_pedido_status | data |
+-------+------------------+---------------------+
| 86745 | 1 | 2017-11-07 10:59:51 |
| 86622 | 1 | 2017-11-04 12:23:21 |
| 86291 | 1 | 2017-10-30 11:16:12 |
+-------+------------------+---------------------+
What do I need to change in SQL?
Thanks and sorry my english...

Query date from same column with several conditions

I want to do a query to see when a client is disconnected, between January and February, by a cable company and if they got connect again until 7 days after the disconnection.
My table would be something like this:
+---------+----------+------------+------------+
| client | ID_Order | Work_Order | Date_Order |
+---------+----------+------------+------------+
| Client1 | 123AB | Disconnect | 20/01/2017 |
| Client1 | 234EA | Connect | 22/01/2017 |
| Client2 | 242FA | Connect | 30/01/2017 |
| Client3 | 244FE | Disconnect | 30/01/2017 |
| Client2 | 301EA | Disconnect | 10/02/2017 |
| Client4 | 355AD | Disconnect | 20/02/2017 |
| Client4 | 368AD | Connect | 25/02/2017 |
| Client5 | 401AD | Connect | 05/03/2017 |
| Client6 | 440AD | Disconnect | 15/03/2017 |
+---------+----------+------------+------------+
And I want to transform the previous table in a table like this: one row per client, with the connect order and disconnect order in 2 different columns
+---------+-----------------+-------------+--------------+------------+
| client | ID_Order_Discon | Date_Discon | ID_Order_Con | Date_Con |
+---------+-----------------+-------------+--------------+------------+
| Client1 | 123AB | 20/01/2017 | 234EA | 22/01/2017 |
| Client4 | 355AD | 20/02/2017 | 368AD | 25/02/2017 |
+---------+-----------------+-------------+--------------+------------+
My problem:
1- How make the query inside the same columns (Date_Order), to choose the clients that where connected after 7 days the disconnection. Guess need to use DATEDIFF()?
2- how to put the orders of the same client in 2 different columns? Guess I have to do an IF()/CASE () and a join ()?
To Combine both table you can use:
select c1.client, cq.ID_Order ID_Order_Discon , c1.Date_Order as Date_discon, c2.ID_Order ID_Order_Con, c2.Date_order Date_Con
from table c1 join table c2 on t1.client = t2.client and t1.work-order = 'disconnect' and t2.work_order = 'Connect'
and for the frst question you can use where DATEDIFF(c1.date_order,c2.dateOrder)>7

How do I get information from multiple MySQL tables?

I'm working on a way to keep track of physical connections between servers/switches in multiple racks. I set up my database so it is like this:
mysql> show tables;
+----------------------+
| Tables_in_System |
+----------------------+
| connections |
| racks |
| servers |
+----------------------+
mysql> select * from racks;
+----+-------------+-------------+----------+
| id | rack_number | rack_height | location |
+----+-------------+-------------+----------+
| 2 | 7430 | 43 | xxx |
| 3 | 7431 | 43 | xxx |
| 4 | 7432 | 43 | xxx |
+----+-------------+-------------+----------+
mysql> select * from servers;
+----+-------------+---------------+-----------------+---------------+
| id | server_type | rack_location | server_unit_loc | server_height |
+----+-------------+---------------+-----------------+---------------+
| 1 | Server 1 | 7430 | 2 | 3 |
| 2 | Cisco 2960 | 7431 | 1 | 1 |
| 3 | Server 2 | 7431 | 9 | 1 |
| 15 | Server 3 | 7432 | 27 | 2 |
| 16 | Cisco 2248 | 7432 | 29 | 1 |
+----+-------------+---------------+-----------------+---------------+
rack_location refers to the numbered racks in the first table.
server_unit_loc is the placement of the server in the rack, 1 being the top, 43 would be the bottom. And server_height is just how tall the server is.
mysql> select * from connections;
+----+----------+----------+------------+------------+---------+---------+------------+
| id | rack_id1 | rack_id2 | server_id1 | server_id2 | port_s1 | port_s2 | cable_type |
+----+----------+----------+------------+------------+---------+---------+------------+
| 1 | 7430 | 7430 | 2 | 1 | J01 | J08 | RJ-45 |
| 2 | 7430 | 7431 | 2 | 3 | J03 | J08 | RJ-45 |
| 3 | 7430 | 7432 | 2 | 15 | J02 | J09 | SFP+ |
+----+----------+----------+------------+------------+---------+---------+------------+
rack_id's again (I realize now these are probably pointless...) server_id1 & 2 are the id's from the servers table. ports_1&2 correlate to the jack the cable is plugged in. This is an internal system we use, so J01 means it is the first jack on the server, and it plugs into the 8th(J08) port on the connecting server.
The end goal here is to be able to quickly see how many servers are connected to any chosen server and also the details about that connection info. In this example I connected the Cisco 2960 to multiple servers in different racks.
+What kind of query would I have to put in to return all the servers that are connected to the Cisco (2). I'd like to be able to specify just the server_id# and it would go to the different tables and get all the information such as which rack it's in, it's type, and the height of the server
Cisco2960 7430 A01 J01 --> Server1 7430 A02 J08 "RJ-45"
Cisco2960 7430 A01 J03 --> Server2 7431 A09 J08 "RJ-45"
Cisco2960 7430 A01 J02 --> Server3 7432 A27 J09 "SFP+"
Something like that...^
Thanks! Also if you have a better idea of how to go about accomplishing this I'd love to hear everyone's thoughts.
You would need to create query that joins the related information together.
Think of label S being (source server). Label T being (target server).
Ex.
SELECT S.server_type, S.rack_location, CONCAT('A0',S.server_unit_loc), B.port_s1,T.server_type, T.rack_location, CONCAT('A0',T.server_unit_loc), B.port_s2,B.cable_type FROM servers S
JOIN connections B ON B.server_id1 = S.id
JOIN servers T ON B.server_id2 = T.ID
WHERE S.id = 2
I agree with one of the comments. You could normalize things (rid some redundant rack information).

Conditionally move MySQL data between rows in same table

Working in Redmine, I need to copy(not move) data from certain rows to other rows based on matching project id numbers with time entries.
I have included a diagram of the table "custom_values" and my understanding of the design below(CURRENT DATA):
+----+-----------------+---------------+-----------------+-------+
| id | customized_type | customized_id | custom_field_id | value |
+----+-----------------+---------------+-----------------+-------+
| 1 | Project | 1 | 1 | 01 |
| 2 | TimeEntry | 1 | 4 | 01 |
| 3 | Project | 2 | 1 | 02 |
| 4 | TimeEntry | 2 | 4 | 02 |
| 5 | Project | 3 | 1 | 03 |
| 6 | TimeEntry | 3 | 4 | |
| 7 | Project | 4 | 1 | 04 |
| 8 | TimeEntry | 4 | 4 | |
+----+-----------------+---------------+-----------------+-------+
At the risk of oversimplifying,
"id" = The primary key for each entry in custom_values
"customized_type" = Specifies which db table the row is referring to.
"customized_id" = Specifies the primary key for the db table entry previously specified in "customized_type".
"custom_field_id" = Specifies which custom field the row is referring to. Redmine admins can arbitrarily add and remove custom fields.
"value" = The data contained within the custom field specified by
"custom_field_id"
In my situation, the values listed in "value" are representing unique customer id numbers. The customer id numbers did not always get entered with each time entry. I need to copy the customer numbers from the project rows to the matching time entry rows. Each time entry has a project_id field.
So far, here is my mangled SQL query:
SELECT
custom_field_id,
custom_values.value AS 'CUSTOMER_NUMBER',
custom_values.customized_id AS 'PROJECT_ID_NUMBER',
custom_values.customized_type,
time_entries.comments AS 'TIME_ENTRY_COMMENTS'
FROM
redmine_tweaking.custom_values
LEFT JOIN
redmine_tweaking.time_entries ON custom_values.customized_id = time_entries.project_id
WHERE
custom_values.customized_type='Project' AND custom_values.custom_field_id=1;
The query I have so far allows me to see that I have the time entries connected properly to their matching projects, but that is all I have been able to figure out. So in other words, this SQL statement does not exactly solve my problem.
Plus, even if it did work, I think the way I laid it out looks like 200 lbs of bird poop. There must be a better/more optimized way to do this.
Any help would be greatly appreciated. I am relatively new and I have been pouring hours into solving this problem.
UPDATE:
Ok, here is the time_entries table:
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
| id | project_id | user_id | issue_id | hours | comments | activity_id | spent_on | tyear | tmonth | tweek | created_on | updated_on |
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
| 1 | 1 | 1 | 1 | .25 | test | 9 | 2015-11-04 | 2015 | 11 | 45 | 2015-11-04 08:18:12 | 2015-11-04 10:18:12 |
| 2 | 2 | 1 | 1 | .25 | test2 | 9 | 2015-11-04 | 2015 | 11 | 45 | 2015-11-04 09:18:12 | 2015-11-04 12:18:12 |
+----+------------+---------+----------+-------+----------+-------------+------------+-------+--------+-------+---------------------+---------------------+
As opposed to the original table that I first posted, the expected output would show this:
+----+-----------------+---------------+-----------------+-------+
| id | customized_type | customized_id | custom_field_id | value |
+----+-----------------+---------------+-----------------+-------+
| 1 | Project | 1 | 1 | 01 |
| 2 | TimeEntry | 1 | 4 | 01 |
| 3 | Project | 2 | 1 | 02 |
| 4 | TimeEntry | 2 | 4 | 02 |
| 5 | Project | 3 | 1 | 03 |
| 6 | TimeEntry | 3 | 4 | 03 |
| 7 | Project | 4 | 1 | 04 |
| 8 | TimeEntry | 4 | 4 | 04 |
+----+-----------------+---------------+-----------------+-------+

MySQL Select Column if Column in another table is zero

My issue is mostly that im not sure how to word what I need in a search.
mysql> SELECT * FROM `accounts`;
+----+--------+---------+----------+
| id | status | oplevel | username |
+----+--------+---------+----------+
| 10 | 1 | 0 | root |
| 11 | 1 | 1 | Xylex |
| 12 | 1 | 16 | Anubis |
| 13 | 0 | 16 | Kami |
| 14 | 1 | 16 | Zorn |
+----+--------+---------+----------+
mysql> SELECT * FROM `networks`;
+-----------+-----------+-------------+-----------------+
| networkid | accountid | networkname | serveraddress |
+-----------+-----------+-------------+-----------------+
| 1 | 10 | Fakenet | irc.fakenet.org |
| 2 | 10 | Undernet | irc.under.net |
| 3 | 12 | Takenet | irc.takenet.com |
| 4 | 13 | Examplenet | irc.example.org |
+-----------+-----------+-------------+-----------------+
I have this accounts table and this networks table
I need a single query that will return the address of the IRC network, ONLY IF the corresponding account id status column is 1 (enabled)
Ive been searching for hours.
What Do?
looks like a simple join to me:
select serveraddress from accounts join networks on id=accountid and id=YOUR_ACCOUNT_ID_HERE
Or
select serveraddress from networks where accountid=YOUR_ID and exists(select * from accounts where id=accountid and status)
Many ways to go about this..
select serveraddress from networks, accounts where networks.accountid = accounts.id and accounts.satus=1;
Is it what you're looking for?
I've updated the query to place account id.
select acc.id, acc.username, nw.serveraddress from accounts acc join networks nw on acc.id = nw.accountid where acc.status == 1 and acc.id=XXX