Why would I use letters in front of each value in my query like this?
In the database, each of these values is WITHOUT the letter in front.
SELECT c.client_id, c.client_name, c.contactperson, c.internal_comment,
IF NULL(r.region, 'Alle byer') as region, c.phone, c.email,
uu.fullname as changed_by,
(select count(p.project_id)
from projects p
where p.client_id = c.client_id and (p.is_deleted != 1 or p.is_deleted is null)
) as numProjects
FROM clients c LEFT JOIN users uu ON c.db_changed_by = uu.id
LEFT JOIN regions r ON c.region_id = r.region_id
WHERE (c.is_deleted != 1 or c.is_deleted is null)
I have tried looking it up, but I can't find it anywhere.
When in SQL you need to use more than one table for a query, you can do this:
SELECT person.name, vehicle.id FROM person, vehicle;
OR you can do it smaller, and put like this
SELECT p.name, v.id FROM person p, vehicle v;
It's only for reducing the query length, and it's useful for you
By "letters in front", I assume you mean the qualifiers on the columns c., uu. and so on. They indicate the table where the column comes from. In a sense, they are part of the definition of the column.
This is your query:
SELECT c.client_id, c.client_name, c.contactperson, c.internal_comment,
IF NULL(r.region, 'Alle byer') as region, c.phone, c.email,
uu.fullname as changed_by,
(select count(p.project_id)
from projects p
where p.client_id = c.client_id and (p.is_deleted != 1 or p.is_deleted is null)
) as numProjects
FROM clients c LEFT JOIN
users uu
ON c.db_changed_by = uu.id LEFT JOIN
regions r
ON c.region_id = r.region_id
WHERE (c.is_deleted != 1 or c.is_deleted is null)
In some cases, these are needed. Consider the on clause:
ON c.region_id = r.region_id
If you leave them out, you have:
ON region_id = region_id
The SQL compiler cannot interpret this, because it does not know where region_id comes from. Is it from clients or regions? If you used this in the select, you would have the same issue -- and it makes a difference because of the left join. This is also true in the correlated subquery.
In general, it is good practice to qualify column names for several reasons:
The query is unambiguous.
You (and others) readily know where columns are coming from.
If you modify the query and add a new table/subquery, you don't have to worry about naming conflicts.
If the underlying tables are modified to have new column names that are shared with other tables, then the query will still compile.
Consider you are accessing 2 tables and both have same column name say 'Id', In query you can easily identify those columns using letters like a.Id == d.Id if first table has alias name 'a' and second table 'b'. Or else It would be very difficult to identify which column belongs which table especially when you have common table columns.
Related
I got this, and I want to get their "company" names for each one.
SELECT `client`.`name`,`client`.`lastname`
FROM `check`,`reserv`,`client`
WHERE `check`.`idReserv`=`reserv`.`id`
AND `reserv`.`idPerson`=`client`.`id`
ORDER BY `check`.`id`
, and I want to get their "company" names for each one, from table "company".
So I tried this:
SELECT `client`.`name`,`client`.`lastname`, `company`.`name`
FROM `check`,`reserv`,`client`,`company`
WHERE `reserv`.`idCompany`=`company`.`id`
AND `check`.`idReserv`=`reserv`.`id`
AND `reserv`.`idPerson`=`client`.`id`
ORDER BY `check`.`id`
but there is some people in the table "reserv" with an "idCompany" inexistent. so with that condition, this query only show me people who has an existent "id" in the table "company". I want to show the people with no company up and the space of company.name in blank if there is no company
I tryed many ways even with joins, but I cannot fix it. I'm tired to write "company" also.
You can use LEFT JOIN for this purpose like-
reserv r LEFT JOIN company c ON r.idCompany = c.id
You should use LEFT join instead.
SQL LEFT JOIN
SELECT c.name, c.lastname, co.name
FROM check AS ck
LEFT JOIN reserv AS r ON(ck.idReserv = r.id)
LEFT JOIN client AS c ON(r.idPerson = c.id)
LEFT JOIN company AS co ON(r.idCompany = co.id)
ORDER BY c.id
The ANSI 89 standard uses , notation for table joins with the criteria of the join being in the where clause. However I don't believe mySQL supports this outer style of join needed to address your problem. To express an outer join in this syntax you would need to use a *= for left join or =* for a right join; but again not sure mySQL supports it.
So in your case:
SELECT `client`.`name`,`client`.`lastname`, `company`.`name`
FROM `check`,`reserv`,`client`,`company`
WHERE `reserv`.`idCompany`*=`company`.`id`
AND `check`.`idReserv`=`reserv`.`id`
AND `reserv`.`idPerson`=`client`.`id`
ORDER BY `check`.`id`
However, I find that notation difficult to read and no need for all the escaping of table/column names (except reserved words)... so the below follows the ANSI 92 standards which allow for the use of INNER and LEFT Join syntax to explicitly define the type of join. Both notations should optimize to the same execution plan so either works (provided mySQL supports the *= notation) as well; it's just a matter of which standard you choose to use.
SELECT client.name
, client.lastname
, company.name
FROM `check`
INNER JOIN reserv
on `check`.idReserv=reserv.id
INNER JOIN client
on reserv.idPerson=client.id
LEFT JOIN company
on reserv.idCompany=company.id
ORDER BY `check`.id
my table user contains these fields
id,company_id,created_by,name,image
table valet contains
id,vid,dept_id
table cart contains
id,dept_id,map_id,purchase,time
to get the details i have written this mysql query
SELECT c.id, a.id, c.purchace, c.time
FROM user a
LEFT JOIN valet b ON a.vid = b.id
AND a.is_deleted = 0
LEFT JOIN cart c ON b.dept_id = c.dept_id
WHERE a.company_id = 18
AND a.created_by = 102
AND a.is_deleted = 0
AND c.time
IN ( SELECT MAX( time ) FROM cart WHERE dept_id = b.dept_id )
from these three table i want to select last updated raw from cart along with id from user table which is mapped in valet table
this query works fine but it takes almost 15 sec to retrieve the details .
is there any way to improve this query or may be i am doing some wrong.
any help would be appreciated
For one thing, I can see that you’re running the subquery for each row. Depending on what the optimiser does, that may have an impact. max is a pretty expensive operation (there’s nothing for it but to read every row).
If you plan to update and use this query repeatedly, perhaps you should at least index the table on cart.time. This will make it much easier to find the maximum value.
MySQL has the concept of user variables, so you can set a variable to the result of the subquery, and that might help:
SELECT c.id, a.id, c.purchace, c.time
FROM
user a
LEFT JOIN valet b ON a.vid = b.id AND a.is_deleted = '0'
LEFT JOIN cart c ON b.dept_id = c.dept_id
LEFT JOIN (SELECT dept_id,max(time) as mx FROM cart GROUP BY dept_id) m on m.dept_id=c.dept_id
WHERE
a.company_id = '18'
AND a.created_by = '102'
AND a.is_deleted = '0'
AND c.time=m.mx;
Note also:
since you’re only testing a single value (max) for c.time, you should be using = not in.
I’m not sure about is why you are using strings instead of integers. I shold have though that leaving off the quotes makes more sense.
Your JOIN includes AND a.is_deleted = '0', though you make no mention of it in your table description. In any case, why is it in the JOIN and not in the WHERE clause?
I have below query which I have written like below. Actually, I want to get two diff. colors from colors table. Please look into it and can you tell me It is optimized way? Can I write below query other optimized way?
SELECT d.*,
(SELECT c.clr_title FROM colors AS c WHERE c.id = d.base_color_id) AS base_color,
(SELECT c.clr_title FROM colors AS c WHERE c.id = d.overlay_color_id) AS overlay_color
FROM indira.dress AS d
WHERE id=669;
Thanks for your help.
Here's another way to get an equivalent result:
SELECT d.*
, b.clr_title AS base_color
, o.clr_title AS overlay_color
FROM indira.dress d
LEFT
JOIN colors b ON b.id = d.base_color_id
LEFT
JOIN colors o ON b.id = d.overlay_color_id
WHERE d.id=669
The correlated subqueries in the SELECT list can be expensive for large sets. But for returning a single row, that's not going to be a performance issue, since those subqueries will get executed only once.
In a more general case, for returning lots of rows, using a JOIN is usually more efficient.
You likely already have suitable indexes. For optimum performance, you'd want an index ON indira.dress(id) (likely already the primary key) and ON colors (id) (again, likely already the primary key). There's likely no performance benefit of adding a covering index.
Here is another option. I don't know what columns you do have on dress table so you will likely have to call the ones you out in select and group but this should work.
Not sure if it would be any faster/slower but wanted to give you more options ;-)
Here it is in sql fiddle where I also show what would happen if null was given for overlay. -> http://sqlfiddle.com/#!2/ebc82/3
SELECT
d.name
,MAX(CASE WHEN d.base_color_id = c.id THEN c.clr_title ELSE NULL END) base_color
,MAX(CASE WHEN d.overlay_color_id = c.id THEN c.clr_title ELSE NULL END) overlay_color
FROM
dress d
INNER JOIN colors c ON
c.Id IN (d.base_color_id, d.overlay_color_id)
WHERE
d.id = 669
GROUP BY
d.name
Since you're restricting to a single record, it's probably just fine. But you could always join against the color table twice, like:
select
d.*
,base_color.clr_title base_color
,overlay_color.clr_title overlay_color
from
indira.dress d
left join
colors base_color on d.base_color_id = base_color.id
left join
colors overlay_color on d.overlay_color_id = overlay_color.id
where
d.id = 669
I am not sure if this is possible, but I want to get all records back, as well as their attachment, if its type (a definition table) is 'main' (if it has an attachment, but it's type is something else, I want it to be NULL.
SELECT r.*
FROM record r
LEFT JOIN attachment d on d.record_id = r.id
LEFT JOIN attachment_type at on d.type_id = at.id
WHERE at.name = "main"
GROUP BY r.id
I would do some redesign of the data here, but that's not possible. Could I use a subquery to get the id before doing the join?
I don't know what your attachment column table looks like (beyond the fact that it has a type column), but something like this should be close, meaning (a) it'll return all rows, and (b) the returned attachment value will be null if the type is main:
SELECT
r.*,
CASE WHEN at.name = 'main' THEN d.whatever ELSE NULL END AS attach_thingie
FROM record r
LEFT JOIN attachment d on d.record_id = r.id
LEFT JOIN attachment_type at on d.type_id = at.id
And, as #FreshPrinceOfSO mentioned in the comment above, I don't see any need for the GROUP BY.
One more thing: based on what I can infer from the query, I don't see any glaring design problems among the three tables. I read it as record can have any number of attachment and attachment has (maybe) a type. If that's what your requirements call for you should be OK.
Addendum: choose a maximum of one attachment per record based on the attachment.id column:
SELECT r.*, d.whatever
FROM record r
LEFT JOIN
(
SELECT attachment.record_id, MAX(attachment.id) AS Max_ID
FROM attachment
INNER JOIN attachment_type at ON attachment.type_id = at.id
WHERE at.name = 'main'
GROUP BY attachment.record_id
) att ON r.id = att.record_id
LEFT JOIN attachment d ON d.id = att.Max_ID
The following query hangs: (although subqueries perfomed separately are fine)
I don't know how to make the explain table look ok. If someone tells me, I'll clean it up.
select
sum(grades.points)) as p,
from assignments
left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID
from assignments
left join grades using (assignmentID)
where ... grades.date <= '1255503600' AND grades.date >= '984902400'
group by assignmentID order by grades.date DESC);
I think the problem is with the first grades table... the type ALL with that many rows seems to be the cause.. Everything is indexed.
I uploaded the table as an image. Couldn't get the formatting right:
http://imgur.com/AjX34.png
A commenter wanted the full where clause:
explain extended select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC);
See "The unbearable slowness of IN":
http://www.artfulsoftware.com/infotree/queries.php#568
Super messy, but: (thanks for everyone's help)
SELECT *
FROM grades
LEFT JOIN assignments ON grades.assignmentID = assignments.assignmentID
RIGHT JOIN (
SELECT g.gradeID
FROM assignments a
LEFT JOIN grades g
USING ( assignmentID )
WHERE a.classID = '7815'
AND (
a.type =30170
)
AND g.contactID =7141
g.points
REGEXP '^[-]?[0-9]+[-]?'
AND g.points != '-'
AND g.points != ''
AND (
g.pointsposs IS NULL
OR g.pointsposs = ''
)
AND g.date <= '1255503600'
AND g.date >= '984902400'
GROUP BY assignmentID
ORDER BY g.date DESC
) AS t1 ON t1.gradeID = grades.gradeID
Suppose you use a Real Database (ie, any database except MySQL, but I'll use Postgres as an example) to do this query :
SELECT * FROM ta WHERE aid IN (SELECT subquery)
a Real Database would look at the subquery and estimate its rowcount :
If the rowcount is small (say, less than a few millions)
It would run the subquery, then build an in-memory hash of ids, which also makes them unique, which is a feature of IN().
Then, if the number of rows pulled from ta is a small part of ta, it would use a suitable index to pull the rows. Or, if a major part of the table is selected, it would just scan it entirely, and lookup each id in the hash, which is very fast.
If however the subquery rowcount is quite large
The database would probably rewrite it as a merge JOIN, adding a Sort+Unique to the subquery.
However, you are using MySQL. In this case, it will not do any of this (it is gonna re-execute the subquery for each row of your table) so it will take 1000 years. Sorry.
If your subquery performs fine when it is executed separately, then try using a JOIN rather than IN, like this:
select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
join
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC) using (gradeID);
There really isn't enough information to answer your question, and you've put a ... in the middle of the where clause which is weird. How big are the tables involved and what are the indexes?
Having said that, if there are too many terms in an in clause, you can see seriously degraded performance. Replace the use of in with a right join.
For starters, the table as_types in the in clause is not used. Left joining it serves no purpose so get rid of it.
That leaves the in clause having only the assignments and grades table from the outer query. Clearly the wheres the modify assignments belong in the where clause for the outer query. You should move all of the where grades=whatever into the on clause of the left join to grades.
The query is a little tough to follow, but I suspect that the subquery isn't necessary at all.
It seems like your query is basically thus:
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE gradeID IN
(
SELECT grades.gradeID
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE your_conditions = TRUE
);
But, you're not doing anything really fancy in the where clause in the subquery.
I suspect something more like
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
GROUP BY groupings
WHERE your_conditions_with_some_tweaks = TRUE;
would work just as well.
If I'm missing some key logic here please comment back and I'll edit/delete this post.