how to fetch columns from multiple tables using sql - mysql

I am working on railway transport.
I have the following tables:
DAY table
| - id | name |
| 1 | sunday |
| 2 | monday |
| 3 | tuesday|
station table
|- id | name |
| 1 | zaria |
| 2 | kano |
| 3 | minna |
route table
| - id | source_station_id | destination_station_id|
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 3 |
| 4 | 3 | 2 |
| 3 | 3 | 1 |
departure table
| route_id | day_id | departure_time |
| 1 | 2 | 07:00 hrs |
| 1 | 3 | 07:00 hrs |
| 3 | 2 | 15:30 hrs |
route_id, day_id, source_station_id, destination_station_id are all foreign keys referenced from route, day, and station table respectively.
Now... how do I use sql to fetch from these tables..
output to look like the table below.
| source | destination | day | departure_time |

You want to use a JOIN:
SELECT
source.name AS source,
destination.name AS destination,
day.name AS day,
departure.time AS departure_time
FROM departure
JOIN station AS source
ON departure.source_station_id = source.id
JOIN station AS destination
ON departure.source_station_id = destination.id
JOIN day
ON departure.day_id = day.id
WHERE
# any specific criteria you may have, like:
departure.day_id = 1 # for a sunday
Should do it. For more information see MySQL JOIN Syntax
That said, this is a pretty standard question, and I would not be surprised to see it get voted down.
Further, from a structural point of view, you would really be better off storeing the "day" as a DATETIME, and converting this to and from a Human Readable date string on the fly.
As it stands, since you are only doing the day of the week, you have no way of differentiating between this week and last or last year. Have a look at the bottom-half of this comment, where I start talking about spanning periods of time, and discuss the use of DATETIME. Using native data-types is a preferable, both for performance, and ease of use.

Related

Selecting the most recent result from one table joining to another

I have two tables.
One table contains customer data, like name and email address. The other table contains a log of the status changes.
The status log table looks like this:
+-------------+------------+------------+
| customer_id | status | date |
+-------------+------------+------------+
| 1 | Bought | 2018-07-01 |
| 1 | Bought | 2018-07-02 |
| 2 | Ongoing | 2018-07-03 |
| 3 | Ongoing | 2018-07-04 |
| 1 | Not Bought | 2018-07-05 |
| 4 | Bought | 2018-07-06 |
| 4 | Not Bought | 2018-07-07 |
| 4 | Bought | 2018-07-08 | *
| 3 | Cancelled | 2018-07-09 |
+-------------+------------+------------+
And the customer data:
+-------------+------------+
| id | name | email |
+-------------+------------+
| 1 | Alex | alex#home |
| 2 | John | john#home |
| 3 | Simon | si#home |
| 4 | Philip | phil#home |
+-------------+------------+
I would like to select the customer's who have "Bought" in July (07). But exclude customers who's status has changed from "Bought" anything other most recently.
The result should be just one customer (Philip) - all the others have had their status change to something other than Bought most recently.
I have the following SQL:
SELECT
a.customer_id
FROM
statuslog a
WHERE
DATE(a.`date`) LIKE '2018-07-%'
AND a.status = 'Bought'
ORDER BY a.date DESC
LIMIT 1
But that is as far as I have got! The above query only returns one result, but essentially there could be more than one.
Any help is appreciated!
Here is an approach that uses a correlated subquery to get the most recent status record:
SELECT sl.customerid
FROM wwym_statuslog sl
WHERE sl.date = (SELECT MAX(sl2.date)
FROM wwym_statuslog sl2
WHERE sl2.customer_id = sl.customer_id AND
sl2.date >= '2018-07-01' AND
sl2.date < '2018-08-01'
) AND
sl.status = 'Bought'
ORDER BY sl.date DESC
LIMIT 1;
Notes:
Use meaningful table aliases! That is, abbreviations for the table names, rather than arbitrary letters such as a and b.
Use proper date arithmetic. LIKE is for strings. MySQL has lots of date functions that work.
In MySQL 8+, you would use ROW_NUMBER().

MySQL historical data lookup

I've been looking around and trying to get this to work but I can't seem to get it. I have 2 tables:
TABLE: products
| id | name | some more values |
|----|-----------|------------------|
| 1 | Product 1 | Value 1 |
| 2 | Product 2 | Value 2 |
| 3 | Product 3 | Value 3 |
TABLE: value
| pid | value | stamp |
|-----|-----------|------------------|
| 1 | 7 | 2015-07-11 |
| 2 | 4 | 2015-07-11 |
| 3 | 8 | 2015-07-11 |
| 1 | 9 | 2015-07-21 |
| 2 | 4 | 2015-07-21 |
| 3 | 6 | 2015-07-21 |
First table simply has a list of products, second table has a value for each product (by pid), and the timestamp the value. note: timestamps are not every day, nor are they evenly spaced.
What I would like, is a resulting table like this:
| id | name | some more values | value now | value last month |
|----|-----------|------------------|-----------|------------------|
| 1 | Product 1 | Value 1 | 9 | 7 |
| 2 | Product 2 | Value 2 | 4 | 4 |
| 3 | Product 3 | Value 3 | 6 | 8 |
where 'value now' is the value of the newest timestamp, and the 'value last month' is the value of the timestamp closest to the newest timetamp - 30 days. Keep in mind that -30 days might not have a specific timestamp, the query will need to find the closest timestamp. (looking only up or down doesn't matter, it's an approximation.)
I have made some huge queries but I'm pretty sure there must be an easier way... Any help would be appreciated.
Assuming you get last month and year by PHP or by mysql function, here is a not checked query I hope it will work on first time:
SELECT *, v_now, v_lastmonth FROM products p
LEFT JOIN (SELECT `value` AS v_now FROM value ORDER BY stamp DESC) AS v_now ON p.id=v_now.pid
LEFT JOIN (SELECT `value` AS v_lastmonth FROM value
WHERE month(stamp)='$month' AND year(stamp)='$year'
ORDER BY stamp DESC) AS v_now ON p.id=v_now.pid
You can use group by to get one row for each product result.

MySQL - Alternative to GROUP BY without aggregate function?

Imagine an unordered table set up as below:
+----+------------+----------+-----------+
| ID | Project | Resource | StartDate |
+----+------------+----------+-----------+
| 1 | ExtraParts | Mark | 24/01 |
| 2 | ExtraParts | Sam | 22/01 |
| 3 | TimeLabour | Sally | 19/01 |
| 4 | TimeLabour | Sarena | 23/01 |
| 5 | Runway | Olly | 14/02 |
| 6 | Runway | Mary | 14/05 |
+----+------------+----------+-----------+
I would like to order by the earliest StartDate for each project, but still group the resources from each projects together(not sure if im explaining this right, but below is what I would like to achieve)
+----+-------------+-----------+-----------+
| ID | Project | Resource | StartDate |
+----+-------------+-----------+-----------+
| 1 | TimeLabor | Sally | 19/01 |
| 2 | TimeLabor | Sarena | 23/01 |
| 3 | ExtraParts | Sam | 22/01 |
| 4 | ExtraParts | Mark | 24/01 |
| 5 | Runway | Olly | 14/02 |
| 6 | Runway | Mary | 14/05 |
+----+-------------+-----------+-----------+
If I do ORDER BY StartDate, Project then the result will have jumbled up the projects. If i ORDER BY Project, StartDate then the result will be sorted alphabetically by project first and then sort by date within the same project(if that makes sense). As stated above, I would like to order by the earliest StartDate for each project while still grouping the projects together(not aggregate grouping, just one under the other).
Any help is greatly appreciated! :)
something like this? SQL FIDDLE to play with
SELECT project, resource, startdate
FROM testing
ORDER BY project, STR_TO_DATE(StartDate, '%d/%m')
basically your table is storing dates as a string and in order for you to order it you need to convert the strings to recognized dates... so the STR_TO_DATE() is a method to do that :)
You need to calculate the date used for the ordering:
select t.*
from table t join
(select project, min(startdate) as minsd
from table t
group by project
) tp
on t.project = tp.project
order by tp.minsd, t.project;
Note: this assumes that startdate is really stored as a date and not a string. If it is stored as a string, then you should convert it to a date first.

Mysql how to find cumulative total by group

Can anyone help me to sort this out pleaase. i have a episode table and for an episode there will be following appointments . Episode table will be like
+-------------+------------+------------+------------+----------------+------+
| Episode_id | Patientid | St_date | End_date | Status | ... |
+-------------+------------+------------+------------+----------------+------+
| 61112345 | 100001 | 12-01-2010 | | Active | |
| 61112346 | xxxxxx | 20-01-2010 | 10-10-2011 | Withdrawn | |
| ......... | xxxxxxxx | 30-01-2010 | 10-05-2011 | Lost to follow | |
| ......... | xxxxxxxx | 01-02-2011 | Active | Active | |
+-------------+------------+------------+------------+----------------+------+
Status field holds the status of each episode.A episode has 6 appointments , 3 months per appointment. so totally an episode has 18 months . some patient may complete all 6 appointment , some may withdraw in the middle, or some will be lost to follow up. i need to create a dashboard .
Appointment table will have fields for
Appointment_id
PatientId
...
Stats // Completed or pending, which is used for reporting
For example if a patient complete 2 appointment and if he is marked as Withdrawn on episdode which means that he has withdrawn from 3rd visit and active for 2 visits, if we lost to follow him on 5th app, then he will be active for 4app and then he will be added to lost to follow up on 5th visit. if he completes all then he will added to active for all 6 visits. and the report should be like
Report from 01-01-2010 to 31-12-2010
+--------+--------+-------------+----------------+---------+
| | Active | Withdrawn | Lost to follow | Revised |
+------- +--------+-------------+----------------+---------+
| visit1 | 1500 | 30 | 5 | 5 |
| Visit2 | 1800 | 20 | 4 | 3 |
| Visit3 | 1900 | 45 | 3 | 2 |
| Visit4 | 1800 | 34 | 0 | 1 |
| Visit5 | 1900 | 30 | 0 | 1 |
| Visit6 | 1200 | 20 | 0 | 5 |
+--------+--------+-------------+----------------+---------+
Currently we are fetching the query and using loop only we are generating reports like this, but it is taking time to process, is there any way i can achieve using query itself.
It isn't really clear what you want to group by, but I can give you a general answer. After your where clause you can add "group by fieldname order by fieldname" where fieldname is the element you want to count or sum. You can then count(fieldname) or sum(fieldname) to either add or count.
This may be helpful: http://www.artfulsoftware.com/infotree/qrytip.php?id=105

Assistance with database design

I've got a excel sheet that contains all the employees that have worked for my company and is still working for us. It's a sheet of around 200 rows. Each row has basic info, like surname, name, position, qualification etc etc. 16 columns of basic info. Now, the tricky part is this. After the 16 columns, there are months (May-05 up to the present (Apr-12)). Under every month column, an employee either get's a 0 (contract), 1 (permanent), 2 (contract-terminated) or 3 (student).
What would be the best way to do this? I was thinking of 4 tables (listed below), where the one table determines permanently terminated people (for the sake of knowing who was on what type of employment).
MySQL Table: hr_employees
|-----------------|-------|----|----|----|
| employee_number | name | sur| etc| etc|
|-----------------|-------|----|----|----|
| 1 | Dave | F | xx | xx |
|-----------------|-------|----|----|----|
MySQL Table: hr_month
|----|--------|
| id | month |
|----|--------|
| 1 | May-05 |
| 2 | Jun-05 |
|----|--------|
MySQL Table: hr_status
|----|------|------|--------|
| id | e_no | date | status |
|----|------|------|--------|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
|----|------|------|--------|
MySQL Table: hr_terminated
|----|------|
| id | e_no |
|----|------|
| 1 | 1 |
| 2 | 1 |
|----|------|
I hope you guys understand what I want to achieve, otherwise, ask a question, and I'll answer as best I can! :)
Thanks.
Here is a design that simplifies your data entry and is more relational database like and less Excel like, insofar as it's normalized.
MySQL Table: hr_employee
|-----------------|-------|----|----|----|
| employee_number | name | sur| etc| etc|
|-----------------|-------|----|----|----|
| 1 | Dave | F | xx | xx |
|-----------------|-------|----|----|----|
| 2 | Bob | M | xx | xx |
|-----------------|-------|----|----|----|
MySQL Table: hr_employee_status
|-----------------|------------|------------|--------|
| employee_number | from_date | to_date | status |
|-----------------|------------|------------|--------|
| 1 | 2005-05-01 | 2005-08-31 | 3 |
|-----------------|------------|------------|--------|
| 1 | 2006-05-01 | 2010-02-28 | 0 |
|-----------------|------------|------------|--------|
| 2 | 2010-03-01 | 9999-12-31 | 1 |
|-----------------|------------|------------|--------|
Here you can see that Dave was hired on as a student from May '05 to August '05, then he came back in May '06 as a contract employee which he worked as until the end of February '10. Then on March 1, 2010 Bob was hired as permanent employee and he is still working (max collating date means "until further notice").
The great advantage of this design is that you only have to enter/edit data when something changes, not once a month for every employee that you have or have ever had. You can also see what your workforce looked like at any given date (not just by months!) with a very simple SQL query.