MySQL Query To find Trains between two stations - mysql

My Train Details table,
Table Name- traindetailtb
Id Train_Number Train_Name From To
1 27658 Venad Express ABC XYZ
2 27659 Venad Express XYZ ABC
and my Train Days Table,
Table Name- traindaystb
Id Train_Number Days
1 27658 Sunday
2 27658 Wednesday
3 27659 Saturday
4 27659 Friday
and this my Train Schedule Table,
Table Name- scheduletb
Id Train_Number Station Time
1 27658 ABC 09:00am
2 27658 CDE 10:00am
3 27658 XYZ 11:00am
4 27659 XYZ 12:00pm
5 27659 CDE 01:00pm
6 27659 ABC 02:00pm
There will be two input, From and To
I need a query that gives all Trains(ie,Train_Number,Train_Name,Days) between the given stations
For eg:
For input From- CDE To-XYZ ---->
output will be-- 27658 Venad Express S W
and For input From- XYZ To-CDE ---->
output will be-- 27659 Venad Express M T
Can anyone Please help me to do this.

Are you looking for something like this?
SELECT q.train_number, MAX(t.train_name) train_name, GROUP_CONCAT(LEFT(d.days, 1)) days
FROM
(
SELECT s.train_number
FROM scheduletb s JOIN scheduletb e
ON s.train_number = e.train_number
WHERE s.station = 'CDE' -- from
AND e.station = 'XYZ' -- to
AND s.id < e.id
) q JOIN traindaystb d
ON q.train_number = d.train_number JOIN traindetailtb t
ON q.train_number = t.train_number
GROUP BY q.train_number
Output for CDE -> XYZ:
| TRAIN_NUMBER | TRAIN_NAME | DAYS |
|--------------|---------------|------|
| 27658 | Venad Express | S,W |
Output for XYZ -> CDE:
| TRAIN_NUMBER | TRAIN_NAME | DAYS |
|--------------|---------------|------|
| 27659 | Venad Express | S,F |
Here is SQLFiddle demo

You can try somthing like this:-
SELECT A.Train_number, A.Train_name, C.days
FROM traindetailtb A, traindaystb B, scheduletb C
WHERE C.Train_Number = A.Train_Number
AND A.Train_Number = B.Train_Number
AND FROM = 'ABC' AND TO = 'XYZ';
This might help you.

Try this
SELECT t.Train_Number,t.Train_Name,d.Days
from traindetailtb t,traindaystb d
WHERE
t.Train_Number=d.Train_Number AND From='ABC' AND To='XYZ';
and just read the input and put it into the from=' ' and to=' ';
this single query will return both the output?

i suggest as follow :
select
traindetailtb.Train_Number,
traindetailtb.Train_Name,
traindetailtb.From,
traindetailtb.To,
tranddaystb.Days,
scheduletb.Time
From
traindetailtb
join trandaystb on
traindetailtb.Train_Number=traindaystb.Train_Number
join scheduletb on
traindetailtb.Train_Number=scheduletb.Train_Number and
traindetailtb.From=scheduletb.From
where
traindetailtb.From=$inputFrom
and
traindetailtb.To=$inputTo

Using a self join you can do this, assuming that the Time is really a time field
SELECT a.Train_Number, d.Train_Name, GROUP_CONCAT(e.Days)
FROM scheduletb a
INNER JOIN scheduletb b
ON a.Train_Number = b.Train_Number
AND b.Time > a.Time
INNER JOIN traindetailtb d
ON a.Train_Number = d.Train_Number
INNER JOIN traindaystb e
ON a.Train_Number = e.Train_Number
WHERE a.Station = 'CDE'
AND b.Station = 'XYZ'
GROUP BY a.Train_Number, d.Train_Name
assuming that you have a real time field for the time
If you wanted only the trains that went directly from CDE to XYZ with no intermediate stations you could add a LEFT OUTER JOIN to the table looking for stations with a time between the other 2:-
SELECT a.Train_Number, d.Train_Name, GROUP_CONCAT(e.Days)
FROM scheduletb a
INNER JOIN scheduletb b
ON a.Train_Number = b.Train_Number
AND b.Time > a.Time
LEFT OUTER JOIN scheduletb c
ON a.Train_Number = c.Train_Number
AND c.Time > a.Time
AND c.Time < b.Time
INNER JOIN traindetailtb d
ON a.Train_Number = d.Train_Number
INNER JOIN traindaystb e
ON a.Train_Number = e.Train_Number
WHERE a.Station = 'CDE'
AND b.Station = 'XYZ'
AND c.Station IS NULL
GROUP BY a.Train_Number, d.Train_Name

If you want to visualize the days in one single cell instead of spread into multiple rows, use GROUP_CONCAT, which acts as a aggregator-function.
select tde.train_number, tde.train_name, GROUP_CONCAT(tda.days, ",") as days
from traindetailtb tde
join traindaystb tda on tda.train_number=tde.train_number
where tde.from = $user_input_from
and tde.to = $user_input_to
group by tde.train_number, tde.train_name

Related

Select on 2 tables return no result?

I have 2 tables :
Table 'annonce' (real estate ads) :
idAnnonce | reference
-----------------------
1 | dupond
2 | toto
Table 'freeDays' (Free days for all ads) :
idAnnonce | date
-----------------------
1 | 2015-06-06
1 | 2015-06-07
1 | 2015-06-09
1 | 2015-06-10
2 | 2015-06-06
2 | 2015-06-07
2 | 2015-06-12
2 | 2015-06-13
I want to select all alvailable ads who have only free days between a start and end date, I have to check each days between this date.
The request :
SELECT DISTINCT
`annonce`.`idAnnonce`, `annonce`.`reference`
FROM
`annonce`, `freeDays`
WHERE
`annonce`.`idAnnonce` = `freeDays`.`idAnnonce`
AND
`freeDays`.`date` = '2015-06-06'
AND
`freeDays`.`date` = '2015-06-07'
Return no result. Where is my error ?
It cant be equal both dates
SELECT DISTINCT a.idAnnonce, a.reference
FROM annonce a
INNER JOIN freeDays f ON a.idAnnonce = f.idAnnonce
WHERE f.date BETWEEN '2015-06-06' AND '2015-06-07'
What Matt is say is correct. You can also do this as alternative:
SELECT DISTINCT a.idAnnonce, a.reference
FROM annonce a
INNER JOIN freeDays f ON a.idAnnonce = f.idAnnonce
WHERE f.date IN('2015-06-06','2015-06-07')
Or like this:
SELECT DISTINCT a.idAnnonce, a.reference
FROM annonce a
INNER JOIN freeDays f ON a.idAnnonce = f.idAnnonce
WHERE f.date ='2015-06-06' OR f.date ='2015-06-07'
This will give you the same result as with an BETWEEN
Your WHERE clause is asking for the impossible!
You are asking for rows where the 'freedays'.'date' value is both 2015-06-06 and 2015-06-07.
AND
freeDays.date = '2015-06-06'
AND
freeDays.date = '2015-06-07'
You need to use BETWEEN:
freeDays.date BETWEEN '2015-06-06' AND '2015-06-07'
AND
freeDays.date = '2015-06-06'
OR
freeDays.date = '2015-06-07'

MySQL merge table, with zero vaules

Table A name is source
ID | date | valueS | commonID
1 26.8.14 Svalue01 11
2 21.8.14 Svalue02 11
3 25.8.14 Svalue03 11
Table B name is destination
ID | date | valueD | commonID
1 26.8.14 Dvalue01 11
2 21.8.14 Dvalue03 11
3 24.8.14 Dvalue03 11
So currently im using
SELECT a.*, b.* FROM (SELECT * FROM Source WHERE commonID = '11')a JOIN destination b ON a.commonID = b.commonID
But this dont get me the wished result.
i want something sorted by date, and if there is no record for both on the date, one is zero.
example how it should look
ID | date | valueD | commonID | ID | date | valueS | commonID
1 26.8.14 Dvalue01 11 1 26.8.14 Svalue01 11
3 25.8.14 Svalue03 11
3 24.8.14 Dvalue03 11
2 21.8.14 Dvalue03 11 2 21.8.14 Svalue02 11
Is and how would this be possible?
Additional Info:
-Using Mysql 5.5.37 (MariaDB)
-ID is primary on both
-date fields are "timestamp"
-value fields are INT
-ID fields are INT
-Engine is InnoDB
I hope i provided enough information and tried to make a good explained question
thank you for your help
you want to join on the date as that is the determining column so something like this
SELECT
COALESCE(s.id, "") as s_id,
COALESCE(s.date, "") as s_date,
COALESCE(s.valueS, "") as 'valueS',
COALESCE(s.commonID, "") as s_commonID,
COALESCE(d.id, "") as d_id,
COALESCE(d.date, "") as d_date,
COALESCE(d.valueD, "") as 'valueD',
COALESCE(d.commonID, "") as d_commonID
FROM source s
LEFT JOIN destination d on d.date = s.date
AND d.commonID = s.commonID
WHERE d.commonID = 11
UNION
SELECT
COALESCE(s1.id, "") as s_id,
COALESCE(s1.date, "") as s_date,
COALESCE(s1.valueS, "") as 'valueS',
COALESCE(s1.commonID, "") as s_commonID,
COALESCE(d1.id, "") as d_id,
COALESCE(d1.date, "") as d_date,
COALESCE(d1.valueD, "") as 'valueD',
COALESCE(d1.commonID, "") as d_commonID
FROM source s1
RIGHT JOIN destination d1 on d1.date = s1.date
AND d1.commonID = s1.commonID
WHERE d1.commonID = 11
ORDER BY s_date DESC, d_date DESC
DEMO
You need a Full outer Join
SELECT s.id, s.date, s.valueS, d.valueD, d.commonID FROM source s LEFT JOIN destination d ON (s.id = d.id)
UNION
SELECT s.id, s.date, s.valueS, d.valueD, d.commonID FROM source s RIGHT JOIN destination d ON (s.id = d.id);
I would go with a different solution for this problem. This starts by generating a cross product of all the common ids and dates that you want, and then using left join to bring in the other rows.
You only want one value for commonid, so this is slight overkill for your problem:
select s.*, dest.*
from (select 11 as commonid) c cross join
(select date from source union
select date from destination
) d left outer join
source s
on s.commonid = c.commonid and s.date = d.date left outer join
destination dest
on dest.commonid = c.commonid and dest.date = d.date;
But it is readily extendible. If you wanted two common ids, you could use:
select s.*, dest.*
from (select 11 as commonid union all select 12) c cross join
(select date from source union
select date from destination
) d left outer join
source s
on s.commonid = c.commonid and s.date = d.date left outer join
destination dest
on dest.commonid = c.commonid and dest.date = d.date;

MySQL: get count value in joins and if else

In My application I want to display all my friends and no of cheque given and received
Table Transaction Table Friends
---------------------------- -----------------------------
id given_id rev_id amt id who_id whom_id who_name
----------------------------- -------------------------------
1 2 1 1k 1 1 2 sss
2 2 3 1k 2 3 2 fff
3. 3 2 2k 3 4 1 eee
4 1 2 2k 4 2 1 iii
----------------------------- ------------------------------
Result whom_id=2 name=iii -> Friends (sss,fff)
=> sss gives totally 1 cheque and
sss receives totally 1 cheque and
fff gives 1 cheque and
fff receives 1 cheque and
I tried This one..
SELECT
p.who_id,
p.who_name,
COUNT( r1.give_id ) ,
COUNT( r1.rec_id )
FROM
friends p
LEFT JOIN Transaction r1
ON p.who_id = r1.give_id OR p.who_id = r1.rec_id
WHERE
p.whom_id = 1
GROUP BY p.who_id
Please provide me the best way to do this....
You can do it like this
SELECT
f.who_name,
count(t.given_id) GivenTotal,
count(lt.rev_id) as RecievedTotal
from friends as f
left join transaction as t
on t.given_id = f.who_id
left join transaction as lt
on lt.rev_id = f.who_id
where f.whom_id = 2
group by t.given_id
Demo
Output
who_name | GivenTotal | RecievedTotal
----------------------------------
sss | 1 | 1
fff | 1 | 1
try this,
select
id,
count(given_id) as given,
count(rev_id) as reveive
from Friends
GROUP BY id;
and if you whant the difference between the two amounts try this
select
id,
count(given_id) as given,
count(rev_id) as reveive,
given-receive as diff
from Friends
GROUP BY id;
You can use HAVING with group by ,
instead
WHERE p.whom_id = 1
There are two ways to do this, both involve joining on the Transactions table twice.
You can use subqueries:
SELECT f.who_id,
f.who_name,
t1.TotalGiven,
t2.TotalRev
FROM friends f
LEFT JOIN
(
select count(t.given_id) TotalGiven, t.given_id
from Transactions t
group by t.given_id
) t1
ON f.who_id = t1.given_id
LEFT JOIN
(
select count(t.rev_id) TotalRev, t.rev_id
from Transactions t
group by t.rev_id
) t2
ON f.who_id = t2.rev_id
where f.whom_id = 2;
See SQL Fiddle with Demo.
Or you can just use a join with no subquery:
select f.who_id,
f.who_name,
count(t1.given_id) TotalGiven,
count(t2.rev_id) TotalRev
FROM friends f
LEFT JOIN Transactions t1
ON f.who_id = t1.given_id
LEFT JOIN Transactions t2
ON f.who_id = t2.rev_id
where f.whom_id = 2
group by f.who_id, f.who_name
See SQL Fiddle with Demo
Finally I got the answer,
SELECT
p.who_id, p.who_name, COUNT( r1.give_id ) , COUNT( r2.rec_id )
FROM
friends p
LEFT JOIN Transaction r1 ON p.who_id = r1.give_id
LEFT JOIN Transaction r2 on p.who_id = r2.rec_id
WHERE
p.whom_id = 1
GROUP BY p.who_id

difficulties getting a 3 table join to return expected results

I'm having some difficulty getting to the bottom of this sql query.
Tables:
--Tickets-- --Finance-- --Access--
id_tickets id_finance id_access
name_tickets id_event id_event
cat_tickets id_tickets id_tickets
sold_finance scan_access
Finance and Access both contain a row for multiple of each ticket type as listed in tickets.
and I'm trying to get:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 3043 | 2571
season | 481 | 292
comp | 114 | 75
-------------------------------------
total | 3638 | 2938
The closest I've been to the result I've used:
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance INNER JOIN tickets ON finance.id_tickets = tickets.id_tickets
INNER JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235 AND finance.id_event = access.id_event
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
but that just returns:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 4945 | 4437
season | 954 | 599
comp | 342 | 375
-------------------------------------
total | 6241 | 5411
Any ideas where I could be going wrong?
Thanks!
The problem is the relation between access and finance tables, you have to join them. Even if you LEFT JOIN the table the predicate finance.id_event = access.id_event will make it INNER JOIN. As a work around, use UNION like this:
SELECT
tickets.cat_tickets,
SUM(CASE WHEN a.Type = 'f' THEN num ELSE 0 END) AS total_sold,
SUM(CASE WHEN a.Type = 'a' THEN num ELSE 0 END) AS total_scan
FROM tickets
LEFT JOIN
(
SELECT 'f' Type, id_tickets, sold_finance num
FROM finance f
WHERE id_event = 1
UNION ALL
SELECT 'a', id_tickets, scan_access
FROM access
WHERE id_event = 1
) a ON a.id_tickets = tickets.id_tickets
GROUP BY tickets.cat_tickets;
SQL Fiddle Demo
Although I am fully clear on what you want, just try this query if the result of this is what you are expecting.
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance LEFT JOIN tickets ON finance.id_tickets = tickets.id_tickets
LEFT JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
Disclaimer: This query is not tested due to incomplete data on the question.
SELECT z.Cat_tickets,
COALESCE(x.total_sold,0) total_sold,
COALESCE(y.total_scan,0) total_scan
FROM tickets z
LEFT JOIN
(
SELECT a.id_tickets,
a.cat_tickets,
SUM(b.sold_finance) total_sold
FROM tickets a
INNER JOIN finance b
ON a.id_tickets = b.id_tickets
WHERE id_event = 235
GROUP BY a.id_tickets, a.cat_tickets
) x ON z.id_tickets = x.id_tickets
LEFT JOIN
(
SELECT aa.id_tickets,
aa.cat_tickets,
SUM(bb.scan_access) total_scan
FROM tickets aa
INNER JOIN Access bb
ON aa.id_tickets = bb.id_tickets
WHERE id_event = 235
GROUP BY aa.id_tickets, aa.cat_tickets
) y ON z.id_tickets = y.id_tickets

Getting JOIN and WHERE to work together

Ok, I have an example table with the following information and query.
First up is the data, with the question following at the end.
Here's the SQL Dump:
http://pastie.org/private/o7zzajdpm6lzcbqrjolgg
Or you can use the included a visual below:
Purchases Table
| id | brand | date |
1 b1 2000-01-01
2 b1 2000-01-03
3 b2 2000-01-04
4 b3 2000-01-08
5 b4 2000-01-14
Owners Table
id | firstname | lastname | purchaseid | itemCoupon | itemReturned | Accessories
1 Jane Doe 1 yes no 4
2 Jane Doe 2 yes no 2
3 Jane Doe 3 no no 1
4 Jane Doe 4 no no 3
5 Jane Doe 5 no yes 6
The Query
SELECT brand, COALESCE( SUM( inTime.Accessories ) , 0 ) AS acessory_sum
FROM purchases
INNER JOIN owners AS person ON person.purchaseid = purchases.id
AND person.firstname = 'Jane'
AND person.lastname = 'Doe'
LEFT JOIN owners AS inTime ON person.id = inTime.id
AND purchases.date
BETWEEN DATE( '2000-01-01' )
AND DATE( '2000-01-05' )
GROUP BY purchases.brand
This gives the following expected result:
| brand | accessory_sum
b1 6
b2 1
b3 0
b4 0
The question
Now, I would like to add to the query:
WHERE itemCoupon = 'yes' OR itemReturned = 'yes'
But this overrides the last join and when I do the same search above I get:
| brand | accessory_sum
b1 6
b2 1
Similarly I still want it to return No results found for 2000-01-04, 2000-01-08 using WHERE itemCoupon = 'yes' OR itemReturned = 'yes'. Removing the WHERE gives me zeros for all brands if I try to do it another way.
Basically I want to keep the way the WHERE behaves but also keep the format that I described in the first example of the expected output.
As it is now, using WHERE destroys the way the last LEFT JOIN works with COALESCE which fills the remaining brand rows with zeros.
Your WHERE turns the outer join into an inner join.
You need to move your additionally condition into the LEFT JOIN condition:
LEFT JOIN owners as inTime
ON person.id = inTime.id
AND purchases.date between purchases.date DATE ('2000-01-01') and DATE ('2000-01-05')
AND (inTime.itemCoupon = 'yes' or inTime.itemReturned = 'yes')
the ON clause when doing a JOIN is similar to the WHERE clause. So instead of trying to use WHERE, just add another AND to your query (and don't forget to use the parenthesis in the OR clause):
SELECT brand,
COALESCE(SUM(Time.purchasedAccessories),0) as acessory_sum
FROM purchases
INNER JOIN owners AS person
ON person.purchaseid = purchases.id
AND person.firstname = 'Jane'
AND person.lastname = 'Doe'
AND (person.itemCoupon = 'yes' OR person.itemReturned = 'yes')
LEFT JOIN owners AS inTime
ON person.id= inTime.id
AND purchases.date
BETWEEN purchases.date
DATE( '2000-01-01' )
AND
DATE( '2000-01-05' )
GROUP BY purchases.brand