MYSQL selecting new column based on multiple joins - mysql

I'm still working through some kinks with MySQL so any help will be appreciated.
I have 3 tables -- equipment, states, zones.
equipment:
+---------------+------+------------+
| current_state | id | ...columns |
+---------------+------+------------+
states:
+----------+-------------+
| state | zone_id |
+----------+-------------+
zones:
+-----+------+
| id | zone |
+-----+------+
In equipment, there is one current_state per row.
In states, there is one zone_id per row.
In zones, there is one zone per row.
I would like to JOIN the three tables as a subquery select statement (not even sure if that's a thing) and have the output return as 1 alias'd column among the other columns I'm selecting
+--------------+-------------+
| current_zone | ....columns |
+--------------+-------------+
A sample expected output is:
+------------+-------------+--------+------------------+--------------+---------+
| c_id | g_id | e_id | equipment_type | impressionId | email |
+------------+-------------+--------+------------------+--------------+---------+
| 1234 | ABC1234 | 0001 | VEST | 2032 |ab#yc.com|
| 1234 | 1234ABC | 0001 | SHIRT | 4372 |ab#yc.com|
| 1234 | DCBA123 | 0001 | CAN | 4372 |ab#yc.com|
| 1234 | DCBA321 | 0001 | JACKET | ab#yc.com |ab#yw.com|
| 4567 | abc321d | 0002 | SHIRT | 2032 |db#yw.com|
| 4567 | cba123d | 0002 | CAN | 4372 |db#yw.com|
| 4567 | def4rg4 | 0002 | JEANS | 3210 |db#yw.com|
+------------+-------------+--------+------------------+--------------+---------+
The current query has multiple joins already referring to the zones and states table in order to determine a different value:
SELECT equipment.*,
...
FROM equipment
LEFT JOIN c on equipment.c_id = c.id
LEFT JOIN g on equipment.g_id = g.id
LEFT JOIN states on g.state = states.state
LEFT JOIN zones on zones.id = states.zone_id
Essentially, what I want to do is create a subquery in order to create a new column based on the results of the three joins, something like this:
SELECT equipment.*,
(SELECT
equipment.current_state
FROM equipment
LEFT JOIN equipment.current_state = states.state
LEFT JOIN zones.id = states.zone_id
) as current_zone,
...
This is even possible? Am I trying to select a new column in the wrong place?

Thanks to #TheImpaler I was able to clear up my Scalar Subquery. In my eyes, I thought I had to create another join based on the properties I wanted when in reality all I had to do was create a conditional scalar subquery:
SELECT equipment.*,
(SELECT zones.zone
FROM zones
WHERE equipment.current_state = states.state
AND zones.id = states.zone_id
) as current_zone,
...

Related

Mysql, select from table multiple values

I have two tables in my database.
Couples table:
+---------------------------------------+
| ID | Partner 1 | Partner 2 |
+---------------------------------------+
| 1 | 101 | 102 |
+---------------------------------------+
B table:
+--------------------------------------------------------------
| ID | name | date | Letter | Phonenumber
+--------------------------------------------------------------
| 101| Mark | 1/1/2001 | D | 061234
| 102| lisa | 1/1/2002 | E | 061235
I cant quite figure out how to do the following:
Select from couples table the partners 1 & 2.
From partner 1, grab the name, date, letter & phonenumber.
From partner 2, grab the name, letter and date.
Do this for every couple in the couples table
I cant figure out how to do this, anyone know how? I have been stuck for quite some time now haha.
something like this
+------------------------------------------------------------------------
| ID | name | date | Letter | Phonenumber | name | Letter| date
+------------------------------------------------------------------------
| 1 | Mark | 1/1/2001 | D | 061234 | lisa | E | 1/1/2002
+------------------------------------------------------------------------
You need to join twice - once for each partner. e.g.:
SELECT p1.id, p1.name, p1.date, p1.letter, p1.phonenumber, p2.name, p2.letter, p2.date from couples
JOIN B on (B.id = partner_1) as p1
JOIN B on (B.id = partner_2) as p2
My syntax may not be perfect, but it sounds like you know enough SQL to not need a copy+paste solution, just a pointer on how to solve.

Multiple rows with same identifier, how to select the row with heighest value (ignoring the rest) with multiple left joins?

this issue has been bothering me for a few hours now. After finding out my old query had an issue, I had to rebuild it.
The situation:
I need to match each patient_id with a clinic_id, and for that I get all the appointments using the patient_id,
find the highest appointment_id and use its clinic_id to set the last known clinic_id.
My old query did this, but it skipped patients that never had an appointment.
These are my current results, but I need to filter my results. Question is, how?
+---------------+-------------------+-------------------+---------------+
| patient_id | country_code | appointment_id | clinic_id |
+---------------+-------------------+-------------------+---------------+
| 111 | UK | 620 | 3 |
| 111 | UK | 621 | 2 |
| 111 | UK | 1995 | 1 |
| 222 | UK | 609 | 3 |
| 222 | UK | 610 | 2 |
| 333 | UK | null | null |
| 444 | UK | null | null |
+---------------+-------------------+-------------------+---------------+
What I want is the following:
+---------------+-------------------+-------------------+---------------+
| patient_id | country_code | appointment_id | clinic_id |
+---------------+-------------------+-------------------+---------------+
| 111 | UK | 1995 | 1 |
| 222 | UK | 610 | 2 |
| 333 | UK | null | null |
| 444 | UK | null | null |
+---------------+-------------------+-------------------+---------------+
I am using the following query right now:
SELECT
patient.id,
systemcountry.country_code,
appointment_patient.appointment_id,
appointment.clinic_id
FROM
patient
LEFT JOIN
systemcountry ON patient.country_id = systemcountry.id
LEFT JOIN
appointment_patient ON patient_id = patient.id
LEFT JOIN
appointment ON appointment_patient.appointment_id = appointment.id
This was my old query, which had an issue causing it to skip patients that never had an appointment:
SELECT
patient.id AS patient_id,
systemcountry.code AS systemcountry_code,
appointment.clinic_id
FROM
patient
LEFT JOIN
systemcountry ON patient.land_id = systemcountry.id,
appointment
WHERE
appointment.id = (SELECT
MAX(appointment_id)
FROM
appointment_patient
WHERE
patient_id = patient.id);
I am still a beginner, so go easy on me.
I appreciate any input, thanks!
Move the sub-select in your original query's WHERE clause into a LEFT JOIN (something like this):
LEFT JOIN
(SELECT MAX(appointment_id), patient_id
FROM appointment_patient
GROUP BY patient_id) as apt ON patient.patient_id=apt.patient_id
You can try function max() over columns whose only highest value you want, then group by the result set using patient.id
SELECT
patient.id,
systemcountry.country_code,
max(appointment_patient.appointment_id),
appointment.clinic_id
FROM
patient
LEFT JOIN
systemcountry ON patient.country_id = systemcountry.id
LEFT JOIN
appointment_patient ON patient_id = patient.id
LEFT JOIN
appointment ON appointment_patient.appointment_id = appointment.id
GROUP BY patient.id

Select the most current records from multiple identical rows in the MySQL database

I am working on a product sample inventory system where I track the movement of the products. The status of each product can have a status of "IN" or "OUT" or "REMOVED". Each row of the table represents a new entry, where ID, status and date are unique. Each product also has a serial number.
I need help with a SQL query that will return all products that are currently "OUT". If I simply just select SELECT * FROM table WHERE status = "IN", it will return all products that ever had status IN.
Every time product comes in and out, I duplicate the last row of that specific product and change the status and update the date and it will get a new ID automatically.
Here is the table that I have:
id | serial_number | product | color | date | status
------------------------------------------------------------
1 | K0T4N | XYZ | silver | 2016-07-01 | IN
2 | X56Z7 | ABC | silver | 2016-07-01 | IN
3 | 96T4F | PQR | silver | 2016-07-01 | IN
4 | K0T4N | XYZ | silver | 2016-07-02 | OUT
5 | 96T4F | PQR | silver | 2016-07-03 | OUT
6 | F0P22 | DEF | silver | 2016-07-04 | OUT
7 | X56Z7 | ABC | silver | 2016-07-05 | OUT
8 | F0P22 | DEF | silver | 2016-07-06 | IN
9 | K0T4N | XYZ | silver | 2016-07-07 | IN
10 | X56Z7 | ABC | silver | 2016-07-08 | IN
11 | X56Z7 | ABC | silver | 2016-07-09 | REMOVED
12 | K0T4N | XYZ | silver | 2016-07-10 | OUT
13 | 96T4F | PQR | silver | 2016-07-11 | IN
14 | F0P22 | DEF | silver | 2016-07-12 | OUT
This query will give you all the latest records for each serial_number
SELECT a.* FROM your_table a
LEFT JOIN your_table b ON a.serial_number = b.serial_number AND a.id < b.id
WHERE b.serial_number IS NULL
Below query will give your expected result
SELECT a.* FROM your_table a
LEFT JOIN your_table b ON a.serial_number = b.serial_number AND a.id < b.id
WHERE b.serial_number IS NULL AND a.status LIKE 'OUT'
There are two good ways to do this. Which way is best,in terms of performance, can depend on various factors, so try both.
SELECT
t1.*
FROM table t
LEFT OUTER JOIN table later_t
ON later_t.serial_number = t.serial_number
AND later_t.date > t.date
WHERE later_t.id IS NULL
AND t.status = "OUT"
Which column you check from later_t for IS NULL does not matter, so long as that column is declared NOT NULL in the table definition.
The other logically equivalent method is:
SELECT
t.*
FROM table t
INNER JOIN (
SELECT
serial_number,
MAX(date) AS date
FROM table
GROUP BY serial_number
) latest_t
ON later_t.serial_number = t.serial_number
AND latest_t.date = t.date
WHERE t.status = "OUT"
For each of these queries, I strongly suggest the following index:
ALTER TABLE table
ADD INDEX `LatestSerialStatus` (serial_number,date)
I use this type of query a lot in my own work, and have the above index as the primary key on tables. Query performance is extremely fast in such cases, for these type of queries.
See also the documentation on this query type.

Mysql include column with no rows returned for specific dates

I would like to ask a quick question regarding a mysql query.
I have a table named trans :
+----+---------------------+------+-------+----------+----------+
| ID | Date | User | PCNum | Customer | trans_In |
+----+---------------------+------+-------+----------+----------+
| 8 | 2013-01-23 16:24:10 | test | PC2 | George | 10 |
| 9 | 2013-01-23 16:27:22 | test | PC2 | Nick | 0 |
| 10 | 2013-01-24 16:28:48 | test | PC2 | Ted | 10 |
| 11 | 2013-01-25 16:36:40 | test | PC2 | Danny | 10 |
+----+---------------------+------+-------+----------+----------+
and another named customers :
+----+---------+-----------+
| ID | Name | Surname |
+----+---------+-----------+
| 1 | George | |
| 2 | Nick | |
| 3 | Ted | |
| 4 | Danny | |
| 5 | Alex | |
| 6 | Mike | |
.
.
.
.
+----+---------+-----------+
I want to view the sum of trans_in column for specific customers in a date range BUT ALSO include in the result set, those customers that haven't got any records in the selected date range. Their sum of trans_in could appear as NULL or 0 it doesn't matter...
I have the following query :
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
WHERE Customer IN('George','Nick','Ted','Danny')
AND `Date` >= '2013-01-24'
GROUP BY Customer
ORDER BY input DESC;
But this will only return the sum for 'Ted' and 'Danny' because they only have transactions after the 24th of January...
How can i include all the customers that are inside the WHERE IN (...) function, even those who have no transactions in the selected date range??
I suppose i'll have to join them somehow with the customers table but i cannot figure out how.
Thanks in advance!!
:)
In order to include all records from one table without matching records in another, you have to use a LEFT JOIN.
SELECT
t.`Date`,
c.name,
SUM(t.trans_in) AS 'input'
FROM customers c LEFT JOIN trans t ON (c.name = t.Customer AND t.`Date` >= '2013-01-24')
WHERE c.name IN('George','Nick','Ted','Danny')
GROUP BY c.name
ORDER BY input DESC;
Of course, I would mention that you should be referencing customer by ID, and not by name in your related table. Your current setup leads to information duplication. If the customer changes their name, you now have to update all related records in the trans table instead of just in the customer table.
try this
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
inner join customers
on customers.Name = trans.Customer
WHERE Customer IN('George','Nick','Ted','Danny')
GROUP BY Customer
ORDER BY input DESC;

Finding Max() values via foreign key

Consider this database structure:
__________ __________
| Trucks | | Mileage |
|__________|__________ |__________|________________________
| ID | DRIVER | | TRUCK_ID | MILEAGE | OIL_CHANGE |
|---------------------| |-----------------------------------|
| 1 | Tony | | 1 | 100000 105000 |
| 2 | George | | 2 | 6020 10020 |
| 3 | Mary | | 3 | 37798 41000 |
|_____________________| | 3 | 41233 47200 |
| 3 | 49000 |
|___________________________________|
I want to end up with a result set containing the maximum miles and maximum oil_change for each driver.
_________________________________
| 1 | Tony | 100000 | 105000 |
| 2 | George| 6020 | 10020 |
| 3 | Mary | 49000 | 47200 |
|_______________________________|
This is what I have tried so far:
SELECT t.*, MAX(m.mileage) AS mileage, MAX(m.oil_change) AS oil_change
FROM trucks t
LEFT JOIN mileage m ON t.id = m.truck_id
GROUP BY t.id
But this doesn't seem to allow the MAX function to work properly. It does not always contain the actual maximum value for mileage
Got it! Your mileage column must be defined as a character type, not a numeric type! When than happens, order is done alphabetically, not by value.
You should convert your mileage and oil_change columns to a numeric type (I'd recommend INT based on the data sample provided).
While you don't convert them, this will work:
SELECT t.*, MAX(cast(m.mileage as int)) AS mileage,
MAX(cast(m.oil_change as int)) AS oil_change
FROM trucks t
LEFT JOIN mileage m ON t.id = m.truck_id
GROUP BY t.id
The below queries should work for your question.
SELECT T.DRIVER,MIN(MILEAGE) AS MIN_MILEAGE,MIN(OIL_CHANGE) AS MIN_OIL_CHANGE
FROM TRUCKS T INNER JOIN MILEAGE M
ON T.ID = M.TRUCK_ID
GROUP BY T.DRIVER;
SELECT T.DRIVER,MAX(MILEAGE) AS MAX_MILEAGE,MAX(OIL_CHANGE) AS MAX_OIL_CHANGE
FROM TRUCKS T INNER JOIN MILEAGE M
ON T.ID = M.TRUCK_ID
GROUP BY T.DRIVER;
Regards
Venk