Probably bad title, but I could not find better short description.
These are the tables Im working with:
Table Objects:
ID
NAME
Table Geo:
ID
NAME
PARENT_ID (int)
IDENTIFIER (varchar)
Table ObjectsGeo:
ID
OBJECT_ID
GEO_ID
So I insert some data in "objects":
insert into objects values(1,'Jack');
insert into objects values(2,'Penny');
And then in "geo":
insert into geo values(1,'USA', null, '001');
insert into geo values(2,'New York', 1, '1001');
insert into geo values(3,'England', null, '002');
insert into geo values(4,'London', 3, '1002');
If Jack lives in New York, then I will insert it this way:
insert into ObjectsGeo values(1,1,1);
insert into ObjectsGeo values(2,1,2);
If you tell me this is a bad database design, I will agree (somebody else coded this). However, this is what I have now and making changes in the design could be complicated/cause bugs somewhere else.
Now, when I need to fetch Jack from database, as I also want to know where he lives, I do something quite ugly, which is having a left join to also include "ObjectsGeo". I then only take the first row (sigh) and "manually" put city and country in the object with programming code.
Is there a way, with only one query, to get the row for Jack including 2 fields, one for country and one for city?
----EDIT----
The tables were more or less pseudocode. The query itself (also as pseudocode) should be:
select o.*, g.identifier from objects o left join ObjectsGeo og on og.object_id = o.id left join Geo g on g.id = og.geo_id where o.id = 1;
Following query will get the job done:
select o.name,
(select name from geo where id = min(og.geo_id)) as geo_country,
(select name from geo where id = max(og.geo_id)) as geo_city
from objects o
inner join objectsgeo og
on og.object_id = o.id
inner join geo g
on g.id = og.geo_id
group by og.object_id;
Click here for Demo
If you've any doubt(s), feel free to ask by commenting below.
Hope it helps!
Related
i need help on this. I don't know how to use join in mysql so I try this to avoid to use join.
I have to have in the end this type of table:
tracce.titolo - data - aka (where tracce.idma=artist.id) - aka (for featuring. so i need to achive ida, that's id that identify the featured artist, and i do this under this condition feat.idt=tracce.id (so where the id from the track is the same for the id of the track in the feat table) and where artist.id=feat.ida (so where the id that identify the artist in feat table is the same of the one in the table of artists)).
I try this but it not work, that's the code:
SELECT tracce.titolo, tracce.data, artist.aka, feat.ida FROM tracce, artist, feat
WHERE tracce.idma=artist.id AND feat.idt=tracce.id
SELECT artist.aka FROM tracce
WHERE artist.id=feat.ida
Table structure is:
artist: id - aka
tracce: id - titolo - idma - data (id of track, titolo is title of the track, idma is the id of the main artist and data is date)
feat: idt - ida (idt is the id of the track and ida is the id of the featured artist)
EDIT: modify the code made by Christian, I solve adding another join on artist table using two different alias and it seems to work.
SELECT tracce.titolo, tracce.data, a1.aka as 'main', a2.aka as 'feat' from tracce
JOIN artist a1
ON tracce.idma = a1.id
JOIN feat
ON feat.idt = tracce.id
JOIN artist a2
ON a2.id = feat.ida
What you had in the first statement is an implicit join - not recommended as it will become obsolete. Not exactly sure if I understood, but perhaps this is what you're looking for?
SELECT tracce.titolo, tracce.data, artist.aka, feat.ida from tracce
JOIN artist
ON tracce.idma = artist.id
JOIN feat
ON feat.idt = tracce.id AND artist.id = feat.ida
I am using table aliases to make code shorter.
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';
i have 4 tables in MYSQL and i want to show count of learner_history table's status column district wise for all 36 districts, however when i query it shows only 1 district result even i tried left join to show district names weather there is data available or not it must show null or zero in its position.Data should be like this for all districts:
database structure is like this
[enter image description here][i draw the relation of tables in this image]
please suggest what query would give results like this, thanks.
i draw the relation of tables in this image13.jpg
It was pretty awkward to work from with images, but it should get you what you are asking for. Also wasn't sure what you were wanting with 'Str.#' - the example image you gave with 22 as every result wasn't the most helpful.
select
District.District_Name as District,
sum(Learner_History.`Status` = 'catchment') as Catchment,
sum(Learner_History.`Status` = 'fresher') as Fresher,
sum(Learner_History.`Status` = 'dropout') as Dropout,
sum(Learner_History.`Status` = 'missout') as Missout,
sum(Learner_History.`Status` = 'mainstrain') as Mainstrain,
sum(Learner_History.`Status` = 'pec') as PEC
from District
left join School on School.District_ID = District.District_ID
left join Learner on Learner.School_ID = School.School_ID
left join Learner_History on Learner_History.Learner_ID = Learner.Learner_ID
group by District.District_Name;
I'm trying to avoid using extra() here, but haven't found a way to get the results I want using Django's other queryset methods.
My models relationships are as follows:
Model: Enrollment
FK to Course
FK to User
FK to Mentor (can be NULL)
Model: Course
FK to CourseType
In a single query: given a User, I'm trying to get all of the CourseTypes they have access to. A User has access to a CourseType if they have an Enrollment with both a Course of that CourseType AND an existing Mentor.
This user has 2 Enrollments: one in a Course for CourseType ID 6, and the other for a Course for CourseType ID 7, but her enrollment for CourseType ID 7 does not have a mentor, so she does not have access to CourseType ID 7.
user = User.objects.get(pk=123)
This works fine: Get all of the CourseTypes that the user has enrollments for, but don't (yet) query for the mentor requirement:
In [28]: CourseType.objects.filter(course__enrollment__user=user).values('pk')
Out[28]: [{'pk': 6L}, {'pk': 7L}]
This does not give me the result I want: Excluding enrollments with NULL mentor values. I want it to return only ID 6 since that is the only enrollment with a mentor, but it returns an empty queryset:
In [29]: CourseType.objects.filter(course__enrollment__user=user).exclude(course__enrollment__mentor=None).values('pk')
Out[29]: []
Here's the generated SQL for the last queryset that isn't returning what I want it to:
SELECT `courses_coursetype`.`id` FROM `courses_coursetype` INNER JOIN `courses_course` ON ( `courses_coursetype`.`id` = `courses_course`.`course_type_id` ) INNER JOIN `store_enrollment` ON ( `courses_course`.`id` = `store_enrollment`.`course_id` ) WHERE (`store_enrollment`.`user_id` = 3877 AND NOT (`courses_coursetype`.`id` IN (SELECT U0.`id` AS `id` FROM `courses_coursetype` U0 LEFT OUTER JOIN `courses_course` U1 ON ( U0.`id` = U1.`course_type_id` ) LEFT OUTER JOIN `store_enrollment` U2 ON ( U1.`id` = U2.`course_id` ) WHERE U2.`mentor_id` IS NULL)))
The problem, it seems, is that in implementing the exclude(), Django is creating a subquery which is excluding more rows than I want excluded.
To get the desired results, I had to use extra() to explicitly exclude NULL Mentor values in the WHERE clause:
In [36]: CourseType.objects.filter(course__enrollment__user=user).extra(where=['store_enrollment.mentor_id IS NOT NULL']).values('pk')
Out[36]: [{'pk': 6L}]
Is there a way to get this result without using extra()? If not, should I file a ticket with Django per the docs? I looked at the existing tickets and searched for this issue but unfortunately came up short.
I'm using Django 1.7.10 with MySQL.
Thanks!
Try using isnull.
CourseType.objects.filter(
course__enrollment__user=user,
course__enrollment__mentor__isnull=False,
).values('pk')
Instead of exclude() you can create complex queries using Q(), or in your case ~Q():
filter_q = Q(course__enrollment__user=user) | ~Q(course__enrollment__mentor=None)
CourseType.objects.filter(filter_q).values('pk')
This might lead to a different SQL statement.
See docs:
https://docs.djangoproject.com/en/3.2/topics/db/queries/#complex-lookups-with-q-objects
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.