I have this query I'm trying to build to display specific information for a stored table. I'm needing the query to also display the Enemy Guild Name but I'm having trouble getting the query to take the Enemy Guild ID and link it to the name.
SELECT g.wPos as wPos, g.szGuildName as szGuildName, g.dwGuildExpWeek as dwGuildExpWeek, g.dwEnemyGuildID as dwEnemyGuildID, gm.wPower as wPower, gd.szName as szName
FROM guild as g
LEFT JOIN guild_member AS gm ON gm.dwGuildID = g.dwGuildID AND gm.wPower = '1'
LEFT JOIN gamedata AS gd ON gd.dwID = gm.dwRoleID
WHERE g.wPos = '1'
The output of the query right now results in the following:
Query Results Currently
What I need it to do now is take the dwEnemyGuildID it finds and then use that ID to search for the szGuildName while also displaying the other data it finds.
Use the concept of SELF JOIN, in which we will join same table again if we have a field which is a reference to the same table. Here dwEnemyGuildID is reference to the same table.
A trivial example of the same is finding Manager for an employee from employees table.
Reference: Find the employee id, name along with their manager_id and name
SELECT
g.wPos as wPos,
g.szGuildName as szGuildName,
g.dwGuildExpWeek as dwGuildExpWeek,
g.dwEnemyGuildID as dwEnemyGuildID,
enemy_g.szGuildName as szEnemyGuildName, -- pick name from self joined table
gm.wPower as wPower,
gd.szName as szName
FROM guild as g
LEFT JOIN guild_member AS gm ON gm.dwGuildID = g.dwGuildID AND gm.wPower = '1'
LEFT JOIN guild AS enemy_g ON g.dwEnemyGuildID = enemy_g.dwGuildID -- Use self join
LEFT JOIN gamedata AS gd ON gd.dwID = gm.dwRoleID
WHERE g.wPos = '1';
Related
I'm trying a return a result of the full name of crew members that work on the ship USS Cochrane. I've already made sure that there are some crew members that work on the ship and my code is returning an empty set. Sorry I'm still trying to figure out how to display this code as mysql on here.
SELECT CONCAT(lastName,"",firstName) AS full_name
FROM crewmember INNER JOIN
vessel
ON crewmember.serialNumber = vessel.callsign
WHERE vessel.name = 'USS Cochrane';
Try using the following query.
The query will work only if the value of serialNumber in crewmember table matches the value from callsign in vessel table
SELECT CONCAT(lastName,' ',firstName) AS full_name
FROM crewmember cc JOIN vessel vv
WHERE cc.serialNumber = vv.callsign
AND vv.name = 'USS Cochrane';
Okay, so I know the title is a bit cryptic so I'll do what I can to explain the "problem" I have and the solution I am currently using.
Problem:
An 'object' of work needs to be distributed to the apropriate user based on said object's properties.
The idea is that there is an object of work has properties. Those properties are mapped to skills. A user has skills and is able to work on an object which is within the user's skillset.
There are several [three] property definitions and I currently have the following table structures.
|-- Object to Property Set 1 -- Property Set 1 to Skill --|
Object Table -|-- Object to Property Set 2 -- Property Set 2 to Skill --|-- User Skill -- User Table
|-- Object to Property Set 3 -- Property Set 3 to Skill --|
The query may be a bit easier to understand:
SELECT counts.object_id,
COUNT(DISTINCT counts.object_skill) object_skill_count,
COUNT(DISTINCT counts.user_skill) user_skill_count
FROM
(SELECT object.object_id,
sp.skill_id object_skill,
us.skill_id user_skill
FROM object_table object
LEFT JOIN object_property op ON op.object_id = object.object_id
LEFT JOIN skill_property sp ON sp.property_id = op.property_id
LEFT JOIN user_skill us ON us.skill_id = sp.skill_id
AND us.active = 1
AND us.user_id = {$userid} -- <=- inserted from a PHP script
AND object.state = 1
UNION SELECT object.object_id,
sf.skill_id object_skill,
us.skill_id user_skill
FROM object_table object
LEFT JOIN object_flag obf ON obf.object_id = object.object_id
LEFT JOIN skill_flag sf ON sf.flag_id = obf.flag_id
LEFT JOIN user_skill us ON us.skill_id = sf.skill_id
AND us.active = 1
AND us.user_id = {$userid} -- <=- inserted from a PHP script
AND object.state = 1
UNION SELECT object.object_id,
svf.skill_id object_skill,
us.skill_id user_skill
FROM object_table object
LEFT JOIN object_creator oc ON oc.creator_id = object.creator_id
LEFT JOIN skill_creator sc ON sc.flag_id = oc.flag_id
LEFT JOIN user_skill us ON us.skill_id = sc.skill_id
AND us.active = 1
AND us.user_id = {$userid} -- <=- inserted from a PHP script
AND object.state = 1) counts
GROUP BY counts.object_id
Here we get a count of all the skills an object as well as count the number of skills the user has on that same object. If the two counts match, we know the user can work on the object. If the object's skill count exceeds the user's count, the object is beyond the user's capabilities and will not be assigned to that user.
While the above query works, it slows significantly when thrown at a large[r] table. Would like to know if there is a better way of doing things. And, since the internet is filled with amazing people, here we are.
Retroactive Update:
The Left joins in this case are there because objects can have no properties. This equates to the count 0-0 and thus makes the object workable by anyone.
It looks ok. Conditions placed on data joins instead of where clause, no order by's.
Possible options:
1) Check for missing indexes
http://basitaalishan.com/2013/03/13/find-missing-indexes-using-sql-servers-index-related-dmvs/
2) Change 'left joins' to 'inner joins'
INNER JOIN vs LEFT JOIN performance in SQL Server
3) Use 'UNION ALL' instead of 'Union'
performance of union versus union all
I'm getting the error:
Unknown column event_id in field list
yet I have this column in both tables
SELECT
event_id.events,
tournament.events,
team1.events,
team2.events,event_date.events,
venue.events, picks.pick,tournament.picks
FROM events
INNER JOIN picks
ON `event_id`.events = `event_id`.picks
WHERE tournament = 'SUPER15'
I have checked and tweaked and tried but I just can't figure out what is wrong...
TABLE EVENTS
TABLE PICKS
First should be table name or alias than column name as below
SELECT
events.event_id,
events.tournament,
events.team1,
events.team2,
events.event_date,
events.venue,
picks.pick,
picks.tournament
FROM events
INNER JOIN picks ON
events.`event_id` = picks.`event_id`
WHERE
events.tournament = 'SUPER15'
You have to add column name for condition WHERE tournament = 'SUPER15' too. I changed it to WHERE events.tournament = 'SUPER15'. I also recommend you to use aliases as below
SELECT
E.event_id,
E.tournament,
E.team1,
E.team2,
E.event_date,
E.venue,
P.pick,
P.tournament
FROM events E
INNER JOIN picks P ON
E.`event_id` = P.`event_id`
WHERE
E.tournament = 'SUPER15'
It's more readable, isn't?
You're specifying your selections in reverse order. It should be table.column:
SELECT events.event_id,events.tournament, events.team, events.team2,
events.event_date, events.venue, pick.picks,picks.tournament
FROM events
INNER JOIN picks ON `event_id`.events = `event_id`.picks
WHERE tournament = 'SUPER15'
Table Locations
loc_id loc_name hier2 hier3 hier4 hier5 hier6 hier7 hier8 hier9
152675 Castelli 105 109 0 319 14356 152673 152675 0
14356 Rome 105 109 0 319 14356 0 0 0
...
Table Lacations References
oid name loc_id
12 Demo Villa 152675
...
Now i try to find some entries a user searches by entering the string "caste":
SELECT geo.loc_id, geo.loc_name AS name
FROM locations AS geo
LEFT JOIN locations AS geoh3 ON geo.hier3 = geoh3.loc_id
LEFT JOIN locations AS geoh4 ON geo.hier4 = geoh4.loc_id
LEFT JOIN locations AS geoh8 ON geo.hier8 = geoh8.loc_id
WHERE geo.loc_name LIKE 'caste%'
GROUP BY geo.loc_name
This works. I get the entry with loc_id 152675
Now i only want to get those entrys we have objects in. So i join the reference table:
SELECT geo.loc_id, geo.loc_name AS name
FROM locations AS geo
LEFT JOIN locations AS geoh3 ON geo.hier3 = geoh3.loc_id
LEFT JOIN locations AS geoh4 ON geo.hier4 = geoh4.loc_id
LEFT JOIN locations AS geoh8 ON geo.hier8 = geoh8.loc_id
RIGHT JOIN locations_xref AS gx ON geo.loc_id = gx.loc_id
WHERE geo.loc_name LIKE 'caste%'
GROUP BY geo.loc_name
This works. Again i get the location entry with loc_id 152675 because theres a reference.
PROBLEM
Now the user searches for "rome". I dont get any entry because there is no object reference directly to the city "Rome". The existing object is referenced to a district of rome.
As you can see the district and the city entries have hierarchy IDs which can be used to identify the correct structure. I just cant get it to work together with the reference table, so i only get those objects which are in "rome" or in a district, which is part of rome.
Any help is greatly appreciated!
I don't know much about your data but from first sight it seems that hier6 has the information about relationship between Rome and Castelli. So the query you need may look something more or less like this:
SELECT geo.loc_id, geo.loc_name AS name
FROM locations AS geo
LEFT JOIN locations AS geoh3 ON geo.hier3 = geoh3.loc_id
LEFT JOIN locations AS geoh6 ON geo.hier6 = geoh6.loc_id
LEFT JOIN locations AS geoh4 ON geo.hier4 = geoh4.loc_id
LEFT JOIN locations AS geoh8 ON geo.hier8 = geoh8.loc_id
RIGHT JOIN locations_xref AS gx ON geo.loc_id = gx.loc_id
WHERE geoh6.loc_name LIKE 'rome%'
GROUP BY geo.loc_name
You can tweak your query (as Karolis suggested), but I don't think that will give you what you want. Your query will return 'Castelli' when you search for 'Rome%', but it won't return 'Rome'. It won't return 'Rome', because 'Rome' isn't in your xref table.
To return 'Rome' with this kind of query, you'll need to insert a row for 'Rome' into the xref table.
You can get all the things that are "in" Rome with a UNION, but it doesn't reference your xref table at all.
select la.loc_id, la.loc_name
from locations la
where la.loc_name like 'Rome%'
union
select lb.loc_id, lb.loc_name
from locations lb
inner join locations lc on lc.hier6 = lb.hier6
It's not clear to me how you determine which column to use for the join at run time.
Later . . .
If you don't know which columns to use at run time, you have to LEFT JOIN on all of them. And since you don't know whether the search string might refer to a value joined through column hier2, hier3, hier4, etc., then you have to check each of those columns for a match, too.
If locations_xref acts like a filter, then you need an inner join on that table, along with a row for 'Rome'. (Because you have properties in Rome.) Probably something along these lines.
SELECT geo.loc_id, geo.loc_name AS name
FROM locations AS geo
LEFT JOIN locations AS geoh2 ON geo.hier2 = geoh2.loc_id
LEFT JOIN locations AS geoh3 ON geo.hier3 = geoh3.loc_id
LEFT JOIN locations AS geoh4 ON geo.hier4 = geoh4.loc_id
LEFT JOIN locations AS geoh5 ON geo.hier5 = geoh5.loc_id
LEFT JOIN locations AS geoh6 ON geo.hier6 = geoh6.loc_id
LEFT JOIN locations AS geoh7 ON geo.hier7 = geoh7.loc_id
LEFT JOIN locations AS geoh8 ON geo.hier8 = geoh8.loc_id
LEFT JOIN locations AS geoh9 ON geo.hier9 = geoh9.loc_id
INNER JOIN locations_xref lx on lx.loc_id = geo.loc_id
WHERE geo.loc_name LIKE 'Rom%'
or geoh2.loc_name like 'Rom%'
or geoh3.loc_name like 'Rom%'
or geoh4.loc_name like 'Rom%'
or geoh5.loc_name like 'Rom%'
or geoh6.loc_name like 'Rom%'
or geoh7.loc_name like 'Rom%'
or geoh8.loc_name like 'Rom%'
or geoh9.loc_name like 'Rom%'
Before you get too invested in this model, you should take a look at Bill Karwin's database antipatterns. "Naive Trees" starts on slide 48.
I have a few tables which I have joined together and would like to join a table that has multiple columns. My current query is as follows:
select
usrs.firstname, usrs.middleNames, usrs.surname,
if (usrs.sex=0,'Male','Female') as sex,
usrs.DOB,
(YEAR(CURDATE())-YEAR(usrs.DOB)) - (RIGHT(CURDATE(),5)<RIGHT(usrs.DOB,5)) AS age,
birth.townName AS 'birthTown', birth.regionName AS 'birthRegion', birth.countryName AS 'birthCountry',
location.townName AS 'curTown', location.regionName AS 'curRegion', location.countryName AS 'curCountry',
usrs.email, emails.email AS 'alternateEmail',
numbers.number,
usrs.website,
usrs.aboutMe,
family.mother, family.father, family.partner, marital.status, family.aboutFamily,
children.name AS 'childsName'
from ch09.tbl_users usrs
LEFT JOIN vw_town_region_country birth ON birth.townID = usrs.birthPlace
LEFT JOIN vw_town_region_country location ON location.townID = usrs.currentLocation
LEFT JOIN tbl_alternate_emails emails ON emails.userID = usrs.id
LEFT JOIN tbl_contact_numbers numbers ON numbers.userID = usrs.id
LEFT JOIN tbl_family family ON family.userID = usrs.id
LEFT JOIN tbl_marital_status marital ON family.maritalStatusID = marital.id
LEFT JOIN tbl_children children ON family.id = children.familyID
I put my whole query it might be a bit wrong or cleaner way to do it. The issue is with the tbl_children, as it is "one to many" it results in multiple rows for a single user for every child that user has in the tbl_children table.
So my results are:
userID:1 firstName middleNames surname ....... childsName
userID:1 firstName middleNames surname ....... childsName
userID:1 firstName middleNames surname ....... childsName
I would prefer:
userID:1 firstName middleNames surname ....... childsName childsName2 childsName3
Is it possible to do this through a Join somehow? Obviously it isn't acceptable for me to have multiple entries per user on the view.
You could use the function GROUP_CONCAT in combination with GROUP BY for this. GROUP_CONCAT let's you aggregate values from a column by concatenating them. Note that this will not give you a column for every child, but one column with a string containing all the names.
EDIT; your query would become something like:
select
usrs.firstname, usrs.middleNames, usrs.surname,
if (usrs.sex=0,'Male','Female') as sex,
usrs.DOB, (YEAR(CURDATE())-YEAR(usrs.DOB)) - (RIGHT(CURDATE(),5)<RIGHT(usrs.DOB,5)) AS age,
birth.townName AS 'birthTown', birth.regionName AS 'birthRegion', birth.countryName AS 'birthCountry',
location.townName AS 'curTown', location.regionName AS 'curRegion', location.countryName AS 'curCountry',
usrs.email, emails.email AS 'alternateEmail',
numbers.number,
usrs.website,
usrs.aboutMe,
family.mother, family.father, family.partner, marital.status, family.aboutFamily,
GROUP_CONCAT(children.name SEPERATOR ",") AS 'childsName'
FROM ch09.tbl_users usrs
LEFT JOIN vw_town_region_country birth ON birth.townID = usrs.birthPlace
LEFT JOIN vw_town_region_country location ON location.townID = usrs.currentLocation
LEFT JOIN tbl_alternate_emails emails ON emails.userID = usrs.id
LEFT JOIN tbl_contact_numbers numbers ON numbers.userID = usrs.id
LEFT JOIN tbl_family family ON family.userID = usrs.id
LEFT JOIN tbl_marital_status marital ON family.maritalStatusID = marital.id
LEFT JOIN tbl_children children ON family.id = children.familyID
GROUP BY userID
Assuming that the number of children is unknown at the time of writing the query (i.e., a user could have 0 or 1 or 5 children), making a pivot like this probably isn't the best route for getting data into a front end application.
Depending on how you're accessing the data, you're better off either returning multiple rows per user as you have or retrieving the children (and emails, etc.) for each user as you need them. The key here is to only retrieve them if you need them. I believe that this is known as Lazy Loading in the Object Oriented world.
If you're doing this to fill a list box of some kind, and therefore you need them for each user, then you might consider setting some limit on the number of children that you'll retrieve based on how your list will appear and then use LEFT JOINs to get exactly that number for the rows that you retrieve, rather than doing the round trip to the server for every user.
In other words, it all depends. :)