N1QL Multiple join and sum query producing wrong output - couchbase

I have a bucket containing the following three documents:
coffee {
id
}
cold_coffee {
coffeeId
number
}
warm_coffee {
coffeeId
number
}
I have written the following query in N1QL using Couchbase Server Community Edition 6.0.0:
SELECT META(coffee).id as coffeeId,
SUM(cold_coffee.`number`) as `ccNumber`,
SUM(warm_coffee.`number`) as `wcNumber`,
FROM coffee_bucket coffee
left JOIN coffee_bucket cold_coffee
ON META(coffee).id = cold_coffee.coffeeId
and cold_coffee.type='cold_coffee'
left JOIN coffee_bucket warm_coffee
ON META(coffee).id = warm_coffee.coffeeId
and warm_coffee.type='warm_coffee'
where coffee.type='coffee'
group by META(coffee).id;
I have multiple cold_coffee and warm_coffee documents for every coffee and I need to sum the numbers for all of the cold_coffee and warm_coffee documents. The problem I am having is that if for example I have the following documents:
[
coffee {
id: 1
},
cold_coffee {
coffeeId:1
number:5
},
cold_coffee {
coffeeId:1
number:5
},
warm_coffee {
coffeeId:1
number:10
}
]
My totals are the following:
ccNumber: 10
wcNumber:20
It seems because of the joins the single warm_coffee document is being counted twice?
I came across this site with possibly the same error but unfortunately it's SQL.
And I am unsure how to solve this using N1QL because the right hand term of a JOIN must be a table / bucket as seen in the following post.
Here is a possible solution but I am not sure how to implement this is N1QL?
Can someone please assist?

JOINS can expand the original rows. During first Join left most document is expanded. You are using first document field as second join condition that can produce multiple documents same condition. This how semantics works. You need to adjust your join condition based on the needs.
The second JOIN uses one document from the LEFT most. SUM over DISTINCT may not work because different documents same value (warm_coffee, may under count) is counted once. If I am right you are looking second join to use unique document from LEFT most coffee.
May be you are looking some thing like this
SELECT c.coffeeId,
MAX(c.ccNumber) AS `ccNumber`,
SUM(warm_coffee.`number`) AS `wcNumber`,
FROM ( SELECT META(coffee).id AS coffeeId,
SUM(cold_coffee.`number`) AS `ccNumber`
FROM coffee_bucket coffee
LEFT JOIN coffee_bucket cold_coffee
ON META(coffee).id = cold_coffee.coffeeId AND cold_coffee.type='cold_coffee'
WHERE coffee.type='coffee'
GROUP BY META(coffee).id
) AS c
LEFT JOIN coffee_bucket warm_coffee
ON c.coffeeId = warm_coffee.coffeeId AND warm_coffee.type='warm_coffee'
GROUP BY c.coffeeId;
3 level join
SELECT d.coffeeId,
MAX(c.ccNumber) AS `ccNumber`,
MAX(c.wcNumber) AS `wcNumber`,
SUM(ch.`number`) AS `chNumber`
FROM ( SELECT c.coffeeId,
MAX(c.ccNumber) AS `ccNumber`,
SUM(warm_coffee.`number`) AS `wcNumber`,
FROM ( SELECT META(coffee).id AS coffeeId,
SUM(cold_coffee.`number`) AS `ccNumber`
FROM coffee_bucket coffee
LEFT JOIN coffee_bucket cold_coffee
ON META(coffee).id = cold_coffee.coffeeId AND cold_coffee.type='cold_coffee'
WHERE coffee.type='coffee'
GROUP BY META(coffee).id
) AS c
LEFT JOIN coffee_bucket warm_coffee
ON c.coffeeId = warm_coffee.coffeeId AND warm_coffee.type='warm_coffee'
GROUP BY c.coffeeId) AS d
LEFT JOIN coffee_bucket ch
ON d.coffeeId = ch.coffeeId AND ch.type='chaoc_coffee'
GROUP BY d.coffeeId
;

Related

Issue With Join Doubling Results

I have seen several posts about this on Stack Overflow, but none of them seems to give me an answer that I can understand.
I am trying to join several relations together in order to get all of the relevant information to output all routes that start in China and end in the United States.
In the SeaRoute relation, the start_port and end_port are stored as INT and in the Port relation the pid corresponds to the start_port and end_port and includes a pcountry column.
I am starting off with just trying to output everything that has a start_port that is in China. I am expecting 3 results from my Record relation as those are the only ones that start with China in the table; However, I am receiving 6 records at the output (all of the results appear to have been doubled if I go back and audit what's in the table).
While I want the right answer, I am more concerned that I have a fundamental misunderstanding of Inner Join and the other Join methods. What am I doing wrong?
SELECT *
FROM Record
INNER JOIN Goods AS Go_data
ON Record.gid = Go_data.gid
LEFT JOIN SeaRoute AS SR
ON Record.rid = SR.rid
RIGHT JOIN (SELECT pid, pcountry AS starting_port_country
FROM Port
INNER JOIN SeaRoute AS SR ON Port.pid = SR.start_port
WHERE Port.pcountry = 'China')
AS start_port_table ON SR.start_port = start_port_table.pid
From the looks of your query, you want to be INNER JOINing between the records that you have only on the routes that you want.
You know all of the SeaRoutes that start in China and end in the United States already, you do however need to join to the Ports table twice like so:
SELECT sr.rid,
sp.pcountry AS starting_port_country,
ep.pcountry AS end_port_country
FROM dbo.SeaRoute sr
INNER JOIN dbo.Port sp ON sp.pid = sr.start_port
INNER JOIN dbo.Port ep ON ep.pid = sr.end_port
WHERE sp.pcountry = 'China'
AND ep.pcountry = 'United States'
Then you just need to join that to your main query:
SELECT *
FROM Record
INNER JOIN dbo.Goods AS Go_data ON Record.gid = Go_data.gid
INNER JOIN
(
SELECT sr.rid,
sp.pcountry AS starting_port_country,
ep.pcountry AS end_port_country
FROM dbo.SeaRoute sr
INNER JOIN dbo.Port sp ON sp.pid = sr.start_port
INNER JOIN dbo.Port ep ON ep.pid = sr.end_port
WHERE sp.pcountry = 'China'
AND ep.pcountry = 'United States'
) ports ON ports.rid = Record.rid
There's no way I can explain joins to you any clearer than this page can:
https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins

use multiple results of a query within the query with joins

I have some tables in my database, three main ones and one that holds the many-to-many relations.
1. Student (student_id, student_name)
2. Sport (sport_id, sport_name)
3. Departm (depart_id, depart_name)
4. Sch (sch_id, sch_name)
5. StudSport(relationid, studendid, sportid, departid, schid)
What I want to do is e.g. retrieve the name of the department based on the relations when I know the id. I can get the ids like this:
SELECT departid, schid from studsport
inner join Student on student_id = studentid
inner join Sport on sport_id = sportid
where student_id = 1 and sport_id=2
but I want to get the names of the department and the Sch from their corresponding tables, and I dont know how to do that.
As you don't select anything from Student or Sport, you can remove the corresponding inner joins.
SELECT d.depart_name, sch.sch_name FROM StudSport s
INNER JOIN Sch sch ON s.schid = sch.sch_id
INNER JOIN Departm d ON s.departid = d.depart_id
WHERE s.studendid = 1 AND s.sportid = 2
Something like this???
select sch.sch_nam, departm.depart_name,
-- what you have already --
Left outer Join StudSport on Student.student_id = Studsport.studentid and Sport.sport_id = StudSport.sportid
left outer Join Sch on StudSport.schid = Sch.sch_id
left outer join Departm on studsport.depart_id = studsport.departid
This is untested, a fiddle makes it much easier to give answers because of that.
EDIT - I misread your original query - before the downvotes start to rain - fixing it right now.
The way you should use LEFT OUTER and INNER joins is how the data is meant (again, a fiddle will normally be usefull) but it's just a couple of joins from what you have i guess:
select *
from studsport
join student on studsport.studentid = student.student_id
join sport on studsport.sportid = sport.sport_id
left outer Join Sch on StudSport.schid = Sch.sch_id
left outer join Departm on studsport.depart_id = studsport.departid
where student_id = 1 and sport_id=2

Mysql query with multiple joins and conditions

I am trying to connect four tables using joints,here i used left join to connect tables and my condition is all the goods,item should be same and all the site should be same.same site have multiple goods, so i want to get the sum number of goods from each table. my query is given
select
a.goods
,sum(a.no_of_units) as totala
, a.site
,b.item
,sum(b.quantity) as totalb
,b.site
,c.goods
,c.site
,sum(c.no_of_units) as totalc
,d.site
,d.goods
,sum(d.quantity)b as totald
from
inward_stock a
left join
opening_balance b
on
a.site=b.site
and
a.goods=b.item
left join
return_stock c
on
b.site=c.site
and
b.item=c.goods
left join
stock_consumed d
on
d.site=c.site
and
d.goods=c.goods
Can you put your conditions at the end of the joints like this:
select a.goods,sum(a.no_of_units) as totala, a.site,b.item,sum(b.quantity) as totalb,b.site,c.goods,c.site,sum(c.no_of_units) as totalc,d.site,d.goods,sum(d.quantity) as totald
from inward_stock a left join
opening_balance b on (a.site=b.site) left join
return_stock c on (b.site=c.site) left join
stock_consumed d on (d.site=c.site) where (a.goods=b.item) and (b.item=c.goods) and (d.goods=c.goods)
I have not tested your request but it seems that it's not bad.

Need some assistance with a complex JOIN is SQL Query

Take a look at these tables
It's simple: Venue contains country_ID which is an FK in Society_Territory where we will find a society_ID which is an FK of Society. I have a Venue_ID during the query and my objective is to get the Society_Name but there is a twist but first lets just get the Society_Name
In the following query only look at JOINS and in there I am gonna add comments with this // prefix
SELECT
uuid()AS `UUID`,
`pc`.`PRSClaimID` AS `prsclaimid`,
`a`.`LoginName` AS `loginname`,
`a`.`BandName` AS `bandname`,
`smartistdetails`.`LoginName` AS `createdbyloginname`,
`Society`.`Society_Name` AS societyName
count(
`smliveclaims`.`LiveclaimsID`
)AS `gigcount`
FROM `smprsliveclaimlink`
JOIN `smliveclaims` ON `smprsliveclaimlink`.`fkLiveClaimID` = `smliveclaims`.`LiveclaimsID`
// Here I have the Venue_ID from smliveclaims so i starting moving towards society name
JOIN Venue ON `smliveclaims`.fk_venueId = Venue.Venue_ID
JOIN Society_Territory ON Venue.Country_ID = Society_Territory.Country_ID
JOIN Society ON Society_Territory.Society_Id = Society.Society_ID
// Now from Society i can select the Society_Name which i am already doing in the query above
JOIN `smartistdetails` `a`
JOIN `smprsclaims` `pc` ON `a`.`ArtistID` = `pc`.`fkArtistID`
JOIN `smcategories` ON `pc`.`FK_CategoryID` = `smcategories`.`Id`
JOIN `smcategoriestype` ON `smcategories`.`fk_CategoryTypeId` = `smcategoriestype`.`Id`
JOIN `smartistdetails` ON `pc`.`CreatedBy` = `smartistdetails`.`ArtistID` AND `smprsliveclaimlink`.`fkPRSClaimID` = `pc`.`PRSClaimID`
GROUP BY
`a`.`LoginName`,
`a`.`BandName`,
`smcategories`.`Id`,
`smcategoriestype`.`CategoryType`,
`smartistdetails`.`LoginName`
All is cool till here. Now here is the TWIST
I will have Country_IDs in Venue which will not be in Society_Territory. And I still want to select them and instead of showing and actual Society_Name want to show a word such as "Other"
use a LEFT OUTER JOIN when you link VENUE with SOCIETY_TERRITORY and so on when you link SOCIETY_TERRITORY with SOCIETY
Pay attention: When you use a LEFT OUTER JOIN all tables depends by its must be linked with other LEFT OUTER JOIN because if you use INNER JOIN you cancel di effects on LEFT.
Edit:
SELECT
uuid()AS `UUID`,
`pc`.`PRSClaimID` AS `prsclaimid`,
`a`.`LoginName` AS `loginname`,
`a`.`BandName` AS `bandname`,
`smartistdetails`.`LoginName` AS `createdbyloginname`,
coalesce(`Society`.`Society_Name`, 'Other') AS societyName
count(`smliveclaims`.`LiveclaimsID`)AS `gigcount`
FROM `smprsliveclaimlink`
JOIN `smliveclaims`
ON `smprsliveclaimlink`.`fkLiveClaimID` = `smliveclaims`.`LiveclaimsID`
// Here I have the Venue_ID from smliveclaims so i starting moving towards society name
JOIN Venue ON `smliveclaims`.fk_venueId = Venue.Venue_ID
LEFT OUTER JOIN Society_Territory ON Venue.Country_ID = Society_Territory.Country_ID
LEFT OUTER JOIN Society ON Society_Territory.Society_Id = Society.Society_ID
// Now from Society i can select the Society_Name which i am already doing in the query above
JOIN `smartistdetails` `a`
JOIN `smprsclaims` `pc` ON `a`.`ArtistID` = `pc`.`fkArtistID`
JOIN `smcategories` ON `pc`.`FK_CategoryID` = `smcategories`.`Id`
JOIN `smcategoriestype` ON `smcategories`.`fk_CategoryTypeId` = `smcategoriestype`.`Id`
JOIN `smartistdetails` ON `pc`.`CreatedBy` = `smartistdetails`.`ArtistID` AND `smprsliveclaimlink`.`fkPRSClaimID` = `pc`.`PRSClaimID`
GROUP BY
`a`.`LoginName`,
`a`.`BandName`,
`smcategories`.`Id`,
`smcategoriestype`.`CategoryType`,
`smartistdetails`.`LoginName`
All your JOINs are INNER JOINs. The INNER keyword is optional in MySQL and frequently omitted (as in your example). Use a LEFT OUTER JOIN where required and amend your SELECT clause to include something like "COALESCE(Society_Name,'Other') Society_Name"

I need to finalize this MySQL multiple table JOIN

I have entires, equipments, brands, times and seasons.
entries:
id
time
equipment_1
equipment_2
equipments:
id
id_brand
brands:
id
name
times:
id
id_season
seasons:
id
name
My actual SQL query is:
SELECT entries.*, times.id_season AS id_season
FROM entries, seasons
WHERE entries.time = times.id
But in the final query I need the next information that I don't know how to obtain it:
The name for each entries.equipment_ as equipment_1_name and equipment_2_name which is set in brands.name.
The name of the season as season_name.
Thank you in advance!
Assuming you have normalized data. This avoid costly cartesian joins. I never use cartesian joins myself, although there are some cases where they are useful. Not here, though.
SELECT
entries.*,
times.id_seasons AS id_season,
b1.name AS equipment_1_name,
b2.name AS equipment_2_name,
seasons.name AS season_name
FROM entries
LEFT JOIN equipments AS equipments_1
ON equipments_1.id = entries.equipment_1
LEFT JOIN brands AS brands_1
ON brands_1.id = equipments_1.id_brand
LEFT JOIN equipments AS equipments_2
ON equipments_2.id = entries.equipment_2
LEFT JOIN brands AS brands_2
ON brands_2.id = equipments_2.id_brand
LEFT JOIN times
ON times.id = entries.time
LEFT JOIN seasons
ON seasons.id = times.id_season;