Query on 3 different tables with mySQL - mysql

I've these 3 tables:
___Invoices:
|--------|---------------|
| INV_Id | IVC_BookingId |
|--------|---------------|
| 10 | 31 |
|--------|---------------|
___Bookings:
|--------|-------------|---------------|---------------|
| BOO_Id | BOO_GuestId | BOO_CompanyId | BOO_BillingId |
|--------|-------------|---------------|---------------|
| 10 | 89 90 | 0 |
|--------|-------------|---------------|---------------|
___Kardex:
|--------|----------|-------------|-------------|
| KDX_Id | KDX_Type | KDX_Name | KDX_Company |
|--------|----------|-------------|-------------|
| 89 | guest | Frank | |
| 90 | company | | Google |
|--------|----------|-------------|-------------|
I would like to find for an Invoice the linked card user.
For example, for INV_Id = 10, it should return me:
|--------|-------|---------|---------|
| INV_Id | guest | company | billing |
|--------|-------|---------|---------|
| 10 | Frank | Google | 0 |
|--------|-------|---------|---------|
billing is actually empty because I do not have any existing like between my invoice and booking and kardex.
So my try is the following:
SELECT IVC_Id, IVC_BookingId, IFNULL(BOO_GuestId, 0) AS BOO_GuestId, IFNULL(BOO_CompanyId, 0) AS BOO_CompanyId, IFNULL(BOO_BillingId, 0) AS BOO_BillingId, KDX_Name, KDX_Company
FROM ___Invoices
JOIN ___Bookings
ON ___Bookings.BOO_Id = ___Invoices.IVC_BookingId
LEFT JOIN ___Kardex
ON ___Kardex.KDX_Id = ___Bookings.BOO_BillingId
WHERE IVC_Id='10'
I've no error but I can get the name or company from the ___Kardex table.
Do you know why please ?
Here the SQL Fiddle: http://sqlfiddle.com/#!9/211d01/1
Thanks.

I'm not too sure about your relationships or foreign keys. But from what you have given, I think this would do. Might need slight modifications, but I guess you will get the idea.
SELECT ___Invoices.IVC_Id, k1.KDX_Name as guest, k2.KDX_Company as company
FROM ___Invoices
JOIN ___Bookings
ON ___Bookings.BOO_Id = ___Invoices.IVC_BookingId
LEFT JOIN ___Kardex k1 ON ___Bookings.BOO_GuestId = k1.KDX_Id
LEFT JOIN ___Kardex k2 ON ___Bookings.BOO_CompanyId = k2.KDX_Id
WHERE ___Invoices.IVC_Id='10'
Please check this and let me know if it's what you wanted.
http://sqlfiddle.com/#!9/211d01/31
Feel free to ask if you have any doubts. Hope it helps :)

Please check your second JOIN query:
LEFT JOIN ___Kardex
ON ___Kardex.KDX_Id = ___Bookings.BOO_BillingId
It should be
LEFT JOIN ___Kardex
ON ___Kardex.KDX_Id = ___Bookings.BOO_CompanyId
SQL Fiddle: http://sqlfiddle.com/#!9/211d01/7

You are joining to __Kardex on a BillingID of 0.
0 Does not equal 89 or 90
Your code
LEFT JOIN ___Kardex
ON ___Kardex.KDX_Id = ___Bookings.BOO_BillingId
Here is how I solved: I joined to the same table twice with a different join criteria each time. And then created a new alias for that join:
CREATE TABLE `inv` (`INV_ID` VARCHAR(255) NOT NULL, `IVC_BookingID`
VARCHAR(255) NOT NULL);
CREATE TABLE `bookings` (`BOO_id` VARCHAR(255) NOT NULL, `BOO_guestid` VARCHAR(255) NOT NULL, `BOO_CompanyID` VARCHAR(255) NOT NULL, `BOO_BillingID` VARCHAR(255) NOT NULL);
CREATE TABLE `kardex` (`KDX_ID` VARCHAR(255) NOT NULL, `KDX_TYPE` VARCHAR(255) NOT NULL, `KDX_NAME` VARCHAR(255) NOT NULL, `KDX_COMPANY` VARCHAR(255) NOT NULL);
INSERT INTO `inv` (INV_ID,IVC_BookingID) SELECT '10','31';
INSERT INTO `bookings` (BOO_id,BOO_guestid,BOO_CompanyID,BOO_BillingID) SELECT '10','89','90','0';
INSERT INTO `kardex` (KDX_ID,KDX_TYPE,KDX_NAME,KDX_COMPANY) SELECT '89','guest','Frank','';
INSERT INTO `kardex` (KDX_ID,KDX_TYPE,KDX_NAME,KDX_COMPANY) SELECT '90','company','','Google';
SELECT a.INV_ID, c.kdx_name, d.kdx_company FROM inv a
INNER JOIN bookings b ON b.boo_id=a.INV_ID
INNER JOIN kardex c ON c.kdx_id=b.boo_guestid
INNER JOIN kardex d ON d.kdx_id=b.BOO_CompanyID
WHERE INV_ID='10';
RESULT
INV_ID kdx_name kdx_company
10 Frank Google

You can try
SELECT IVC_Id,
(SELECT KDX_Name FROM ___Kardex WHERE KDX_Id= ___Bookings.BOO_GuestId) AS guest,
(SELECT KDX_Company FROM ___Kardex WHERE KDX_Id= ___Bookings.BOO_CompanyId) AS company,
___Bookings.BOO_BillingId as billing
FROM ___Invoices
JOIN ___Bookings
ON ___Bookings.BOO_Id = ___Invoices.IVC_BookingId
WHERE IVC_Id='10'
It probably not the most optimal solution if your concern is performance, but in you schema design you should not have one table handling more then one type of information, this makes it difficult to write any legit joins.

Related

Two LEFT JOINs in SQL does not preserve data

This query:
SELECT contacts.name, accounts.account
FROM contacts
LEFT JOIN deals
ON contacts.id = deals.contact_id
LEFT JOIN
accounts ON accounts.deal_id = deals.id;
returns:
+------+-------------------+
| name | account |
+------+-------------------+
| Bob | fun deal account |
| Bob | NULL |
| John | NULL |
+------+-------------------+
But I expected:
+------+-------------------+
| name | account |
+------+-------------------+
| Bob | fun deal account |
| Bob | fun deal account |
| John | NULL |
+------+-------------------+
The first LEFT JOIN behaves correctly. Since there are two deals for Bob, Bob correctly shows up twice in result set. But the second LEFT JOIN does not behave right, because the account should have been carried over twice for both Bob records, but instead there is a NULL for the second bob.
The schema:
CREATE TABLE contacts(
id int AUTO_INCREMENT,
name VARCHAR(50),
Primary Key(id)
)
INSERT INTO contacts VALUES('Bob');
INSERT INTO contacts(name) VALUES('John');
CREATE TABLE deals(
id int AUTO_INCREMENT,
name VARCHAR(20),
contact_id int,
FOREIGN KEY(contact_id) REFERENCES contacts(id),
Primary Key(id)
);
INSERT INTO deals(name, contact_id) VALUES('cool deal',1);
INSERT INTO deals(name, contact_id) VALUES('another cool deal',1);
CREATE TABLE accounts(
id int AUTO_INCREMENT,
account VARCHAR(50),
deal_id int,
FOREIGN KEY(deal_id) REFERENCES deals(id),
PRIMARY KEY (id)
)
INSERT INTO accounts(account, deal_id) VALUES('fun deal account', 1);
Why doesn't the second LEFT JOIN give desired behavior and how can I get the 'fun deal account' account to show up for both Bobs?
Bob have two deals but deals.id is auto_increment so fun deal account only match the first row in deals table, the cool deal.
You need to add INSERT INTO accounts(account, deal_id) VALUES('fun deal account', 2); too
In case of doubts, decompose your query.
The first LEFT JOIN could be this:
SELECT contacts.id as contact_id, contacts.name, deals.id as deals_id, deals.name
FROM contacts
LEFT JOIN deals ON contacts.id = deals.contact_id
Which results in :
contact_id name deals_id name
1 Bob 1 cool deal
1 Bob 2 another cool deal
2 John NULL NULL
The second LEFT JOIN is:
LEFT JOIN accounts ON accounts.deal_id = deals.id
So the result given is logical given your data, you have only one account with deal_id=1 so it matches the first row where deals.id=1 : "cool deal" .
I think your mistake is on the last part of your query, the query you wanted is :
SELECT contacts.name, accounts.account FROM contacts LEFT JOIN deals ON contacts.id = deals.contact_id LEFT JOIN accounts ON accounts.deal_id = deals.contact_id
"accounts.deal_id = deals.contact_id" instead of "accounts.deal_id = deals.id" is the deal (pun intended) to have your expected result.

SQL Order by parent and child

Basically I need help in my query here. I want to be in right order which is child must be under parents name and in A-Z order. But if I add a subChild under child (Split 1) seem the order is wrong. It should be under Room Rose.
p/s : A subChild also can create another subChild
HERE I PROVIDE A DEMO
Appreciate your help me get this ordered correctly?
SELECT A.venueID
, B.mainVenueID
, A.venueName
FROM tblAdmVenue A
LEFT
JOIN tblAdmVenueLink B
ON A.venueID = B.subVenueID
ORDER
BY COALESCE(B.mainVenueID, A.venueID)
, B.mainVenueID IS NOT NULL
, A.venueID
I want it return an order something like this.
venueName
--------------
Banquet
Big Room
-Room Daisy
-Room Rose
-Split 1
Hall
-Meeting Room WP
Seem this recursive approach also in not working
WITH venue_ctg AS (
SELECT A.venueID, A.venueName, B.mainVenueID
FROM tblAdmVenue A LEFT JOIN tblAdmVenueLink B
ON A.venueID = B.subVenueID
WHERE B.mainVenueID IS NULL
UNION ALL
SELECT A.venueID, A.venueName, B.mainVenueID
FROM tblAdmVenue A LEFT JOIN tblAdmVenueLink B
ON A.venueID = B.subVenueID
WHERE B.mainVenueID IS NOT NULL
)
SELECT *
FROM venue_ctg ORDER BY venueName
output given
For your data you can use this:
To display this correctly, you can use a SEPARATPR like comma, and split the returned data, and check the hirarchy
-- schema
CREATE TABLE tblAdmVenue (
venueID VARCHAR(225) NOT NULL,
venueName VARCHAR(225) NOT NULL,
PRIMARY KEY(venueID)
);
CREATE TABLE tblAdmVenueLink (
venueLinkID VARCHAR(225) NOT NULL,
mainVenueID VARCHAR(225) NOT NULL,
subVenueID VARCHAR(225) NOT NULL,
PRIMARY KEY(venueLinkID)
-- FOREIGN KEY (DepartmentId) REFERENCES Departments(Id)
);
-- data
INSERT INTO tblAdmVenue (venueID, venueName)
VALUES ('LA43', 'Big Room'), ('LA44', 'Hall'),
('LA45', 'Room Daisy'), ('LA46', 'Room Rose'),
('LA47', 'Banquet'), ('LA48', 'Split 1'),
('LA49', 'Meeting Room WP');
INSERT INTO tblAdmVenueLink (venueLinkID, mainVenueID, subVenueID)
VALUES ('1', 'LA43', 'LA45'), ('2', 'LA43', 'LA46'),
('3', 'LA46', 'LA48'), ('4', 'LA44', 'LA49');
✓
✓
✓
✓
with recursive cte (subVenueID, mainVenueID,level) as (
select subVenueID,
mainVenueID, 1 as level
from tblAdmVenueLink
union
select p.subVenueID,
cte.mainVenueID,
cte.level+1
from tblAdmVenueLink p
inner join cte
on p.mainVenueID = cte.subVenueID
)
select
CONCAT(GROUP_CONCAT(b.venueName ORDER BY level DESC SEPARATOR '-->') ,'-->',a.venueName)
from cte c
LEFT JOIN tblAdmVenue a ON a.venueID = c.subVenueID
LEFT JOIN tblAdmVenue b ON b.venueID = c.mainVenueID
GROUP BY subVenueID;
| CONCAT(GROUP_CONCAT(b.venueName ORDER BY level DESC SEPARATOR '-->') ,'-->',a.venueName) |
| :----------------------------------------------------------------------------------------- |
| Big Room-->Room Daisy |
| Big Room-->Room Rose |
| Big Room-->Room Rose-->Split 1 |
| Hall-->Meeting Room WP |
db<>fiddle here
You want your data ordered in alphabetical order and depth first.
A common solution for this is to traverse the structure from the top element, concatenating the path to each item as you go. You can then directly use the path for ordering.
Here is how to do it in MySQL 8.0 with a recursive query
with recursive cte(venueID, venueName, mainVenueID, path, depth) as (
select v.venueID, v.venueName, cast(null as char(100)), venueName, 0
from tblAdmVenue v
where not exists (select 1 from tblAdmVenueLink l where l.subVenueID = v.venueID)
union all
select v.venueID, v.venueName, c.venueID, concat(c.path, '/', v.venueName), c.depth + 1
from cte c
inner join tblAdmVenueLink l on l.mainVenueID = c.venueID
inner join tblAdmVenue v on v.venueID = l.subVenueID
)
select * from cte order by path
The anchor of the recursive query selects top nodes (ie rows whose ids do not exist in column subVenueID of the link table). Then, the recursive part follows the relations.
As a bonus, I added a level column that represents the depth of each node, starting at 0 for top nodes.
Demo on DB Fiddle:
venueID | venueName | mainVenueID | path | depth
:------ | :-------------- | :---------- | :------------------------- | ----:
LA47 | Banquet | null | Banquet | 0
LA43 | Big Room | null | Big Room | 0
LA45 | Room Daisy | LA43 | Big Room/Room Daisy | 1
LA46 | Room Rose | LA43 | Big Room/Room Rose | 1
LA48 | Split 1 | LA46 | Big Room/Room Rose/Split 1 | 2
LA44 | Hall | null | Hall | 0
LA49 | Meeting Room WP | LA44 | Hall/Meeting Room WP | 1
Use only one table, not two. The first table has all the info needed.
Then start the CTE with the rows WHERE mainVenueID IS NULL, no JOIN needed.
This may be a good tutorial: https://stackoverflow.com/a/18660789/1766831
Its 'forest' is close to what you want.
I suppose you have:
table tblAdmVenue A is the venue list; and
table tblAdmVenueLink B is the tree relation table for parent-child
For your question on how to get a correct sorting order, I think one of the trick is to concatenate the parent venue names.
with q0(venueID, venueName, mainVenueID, venuePath) as (
select
A.venueID,
A.venueName,
null,
A.venueName
from tblAdmVenue A
left join tblAdmVenue B on A.venueID = B.subVenueID
where B.mainVenueID is null
union all
select
A.venueID,
A.venueName,
q0.venueID,
q0.venuePath + char(9) + A.venueName
from q0
inner join tblAdmVenue B on q0.venueID = B.mainVenueID
inner join tblAdmVenue A on A.venueID = B.subVenueID
)
select venueID, venueName, mainVenueID
from q0
order by venuePath

Get the count of the same results in mysql

I want to count all the results that have the same value in a mysql query but no matter what i have tried it does not give me the proper value
+-----+---------------+
| RoomType | ID |
+=====+===============+
| dining room | 2 |
+-----+---------------+
| sleeping room | 2 |
+-----+---------------+
| sleeping room | 2 |
+-----+---------------+
and i want to get the count of the sleeping rooms only.
Here is my query:
SELECT rt.RoomType, r.property_id AS ID
FROM Rooms r
INNER JOIN RoomTypes rt ON r.type = rt.id
WHERE r.property_id = '2'
I have also tried
SELECT SUM(IF(rt.RoomType = 'sleeping room', rt.RoomType, 0))
FROM Rooms r
INNER JOIN RoomTypes rt ON r.type = rt.id
WHERE r.property_id = '2'
but it does not give me the results i want. Any ideas?
I have found the answer and a working way like this:
SELECT SUM(rt.RoomType = 'sleeping room')
FROM Rooms r
INNER JOIN RoomTypes rt ON r.type = rt.id
WHERE r.property_id = '2'
and then it gives me the proper result.
Assuming a table like this:
CREATE TABLE `room_types` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`room_type` varchar(64) DEFAULT NULL,
`room_id` int(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8'
and some data like this:
1245 dining room 2
1246 bath 3
1247 dining room 2
1248 kitchen 5
104 dining room 2
This should work:
select count(*), room_type, room_id from room_types where room_id = 2 group by room_type;
Result:
3 dining room 2
Hope it helps,
f.u.

Join in Mysql query giving wrong output

I have 3 tables.They are as follows.
1.system_user_master
________________________
user_id
user_type
user_registeration_code
user_first_name
user_last_name
2.book_appointment
-----------------------
booking_id
booking_date
puser_id
duser_id
doctor_name
3.routine_queue_patient
----------------------
queue_id
booking_id
queue_checkin_time
puser_id
duser_id
qdoctor_name
Now i want the result like
patient_registeration_code, patient_first_name, patient_last_name, booking_date, queue_checkin_time
In routine_queue_patient booking_id can be null.I want the list of patient of current date of selected doctor who are in routine_queue_patient with booking_id has some value or can be null.if booking_id is null then it shows in booking_date in query result is null and if booking id exists in routine_queue_patient then it display's the booking date.
I have written the query.The result is as follows.
booking_date | quecheck_in_time | user_first_name | user_last_name | user_regis_code
2013-11-12 | 2013-11-12 15:50:53 | rushang | patel | rp9898 |
2013-11-12 | 2013-11-12 16:00:11 | anjana | bhatt | ab9087
The booking_date of rushang patel must come in null as in the routine_queue_patient the booking_id of rushang patel is null but i got the booking_date of second record in front of rushang patel.
The query I have written is as follows.
SELECT DISTINCT b.booking_date
, r.queue_checkin_time
, s.user_first_name
, s.user_last_name
, s.user_registeration_code
FROM routine_queue_patient r
JOIN system_user_master s
ON r.puser_id = s.user_id
JOIN book_appointment b
ON ((b.booking_id = r.booking_id) OR r.booking_id is NULL)
AND DATE(r.queue_checkin_time) = '2013-11-12'
WHERE r.qdoctor_name = 'kashyup Nanavati'
AND DATE(b.booking_date) = '2013-11-12'
Thanks
Rushang
The join with book_appointment table is wrong. You shouldn't try to join on a null value.
Use left join to do it : it will join if a correspondant row is found, else all joined table columns will be null.
=>
SELECT DISTINCT b.booking_date
, r.queue_checkin_time
, s.user_first_name
, s.user_last_name
, s.user_registeration_code
FROM routine_queue_patient r
JOIN system_user_master s
ON r.puser_id = s.user_id
LEFT JOIN book_appointment b
ON (b.booking_id = r.booking_id AND DATE(b.booking_date) = '2013-11-12')
WHERE r.qdoctor_name = 'kashyup Nanavati'
AND DATE(r.queue_checkin_time) = '2013-11-12'

mysql - how to join table twice with the same table

I have this two tables:
Main:
id | name | hair_color | eye_color
1 | a | 1 | 2
2 | b | 1 | 3
3 | c | 4 | 3
Items:
id | name
1 | black
2 | blue
3 | green
4 | blonde
I want to select one row from the Main table but replace the hair_color and eye_color ids by theirs name that fits by the Items table.
I mean, for row number 1 in Main table, I want to get this details:
$res = array(
id=>1,
name=>'a',
hair_color=>'black',
eye_color=>'blue');
I tried this incorrect mysql query:
SELECT `main`.`id`, `main`.`name`, `items`.`name` as `hair_color`, `items`.`name` as `eye_color`
FROM `main`
LEFT JOIN `items` ON `main`.`hair_color` = `items`.`id
LEFT JOIN `items` ON `main`.`eye_color` = `items`.`id
WHERE `main`.`id` = 1
I know that this query is incorrect, but I don't know how to do this right.
Any idea?
Thanks
EDIT:
Thanks all!
You were close, but you needed aliases.
Something like
SELECT `main`.`id`,
`main`.`name`,
h.`name` as `hair_color`,
e.`name` as `eye_color`
FROM `main`
LEFT JOIN `items` h ON `main`.`hair_color` = h.id
LEFT JOIN `items` e ON `main`.`eye_color` = e.id
WHERE `main`.`id` = 1
SQL Fiddle DEMO
You just need to give aliases to your items tables:
SELECT `main`.`id`, `main`.`name`,
hi.`name` as `hair_color`, ei.`name` as `eye_color`
FROM `main`
LEFT JOIN `items` hi ON `main`.`hair_color` = hi.`id`
LEFT JOIN `items` ei ON `main`.`eye_color` = ei.`id`