Could someone please explain this SQL to me, I'm trying to edit code but I don't understand this.
This is the whole sql:
SELECT SQL_CACHE COUNT(c.conversation_id) AS num_messages
FROM table1 AS c
INNER JOIN (
SELECT message_id,conversation_id FROM table2
WHERE recipient_id=:userid ORDER BY created DESC
) AS m ON(m.conversation_id=c.conversation_id)
WHERE (c.initiator_id=:userid OR c.interlocutor_id=:userid)
AND (c.bm_read & IF(c.initiator_id=:userid, :bminit, :bminter)) = 0
AND (c.bm_deleted & IF(c.initiator_id=:userid, :bminit, :bminter)) = 0
GROUP BY c.conversation_id
I don't understand this part:
INNER JOIN (
SELECT message_id,conversation_id FROM table2
WHERE recipient_id=:userid ORDER BY created DESC
) AS m ON(m.conversation_id=c.conversation_id)
WHERE (c.initiator_id=:userid OR c.interlocutor_id=:userid)
AND (c.bm_read & IF(c.initiator_id=:userid, :bminit, :bminter)) = 0
AND (c.bm_deleted & IF(c.initiator_id=:userid, :bminit, :bminter)) = 0
In case you don't understand INNER JOIN, here's a good explanation. It's basically an intersection, produced by doing a CROSS JOIN (i.e. Cartesian Product if you think of the tables as sets) and then filtering based on a condition specified in an ON clause.
The queries are using aliases: c and m respectively to make the overall query shorter. So, c is the result of:
SELECT SQL_CACHE COUNT(c.conversation_id) AS num_messages
FROM table1
and m is the result of:
SELECT message_id,conversation_id FROM table2
WHERE recipient_id=:userid ORDER BY created DESC
and the ON clause is filtering the results of the CROSS JOIN of the two queries based on whether the conversation_id column's value from query c is equal to the conversation_id column's value from query m.
The WHERE, AND and GROUP BY clauses after the INNER JOIN are simply part of the SELECT that are filtering the results after the INNER JOIN occurs.
The IF is pretty simple too:
IF(expr1,expr2,expr3)
If expr1 is TRUE (expr1 <> 0 and expr1 <> NULL) then IF() returns expr2; otherwise it returns expr3.
https://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if
Seems like this would do the same thing...
SELECT COUNT(c.conversation_id) num_messages
FROM conv c
JOIN TBL_MSG m
ON m.conversation_id = c.conversation_id
WHERE m.recipient_id = :userid
AND :userid IN (c.initiator_id,c.interlocutor_id)
AND c.bm_read & IF(c.initiator_id=:userid, :bminit, :bminter)) = 0 -- I don't understand
AND c.bm_deleted & IF(c.initiator_id=:userid, :bminit, :bminter)) = 0 -- this bit.
GROUP
BY c.conversation_id
...although you won't know which conversation_id attained which count!?!
It is doing a subquery (after the keywords INNER JOIN), its result is like "a table" but it is created "on the fly", and its alias is "m".
Once you know how this subquery works, you are doing an inner join of your table "c" with the new table "m" joining by keys m.conversation_id and c.conversation_id. So you are joining conversations from different sources.
The where part is just a filter applied to the output, it depends on the content of the fields.
If you have any other question, just leave a comment ;-)
Related
I am having an issue with my multi join SQL statement, where I need to get the last entries of the table and not the first. I have 3 tables that I try to query based on this statement:
SELECT DISTINCT i.id,i.hz,i.v,i.t,i.u,i.temp, i.c1, s.server, s.hostname, h.codes,s.cpus FROM `dc_servers` AS s INNER JOIN `dc_hardware` AS h ON s.server = h.server INNER JOIN `dc_systemusage` AS i ON s.server = i.server GROUP BY i.server
The tables dc_servers & dc_hardware only has 1 row per server, however the table dc_systemusage has many rows with the same server, as new info is being added.
When I run the query, I get the first entries from the dc_systemusage, but I need to get the latest entries from that table, for me it sounds like I need an ORDER BY, however if I add that to the end of the query like this:
SELECT DISTINCT i.id,i.hz,i.v,i.t,i.u,i.temp, i.c1, s.server, s.hostname, h.codes,s.cpus FROM `dc_servers` AS s INNER JOIN `dc_hardware` AS h ON s.server = h.server INNER JOIN `dc_systemusage` AS i ON s.server = i.server GROUP BY i.server ORDER BY i.id DESC
then I am just ordering the result, which is not what I am looking for.
I hope someone can guide me in the right direction as for how I can get the latest rows and not the first from the table dc_systemusage
I hope I have provided the needed information to guide me, else please do let me know and I will add whatever is neeeded.
Thank you all.
You can find dc_systemusage lastest ids in subquery and use with WHERE ... IN
SELECT i.id,i.hz,i.v,i.t,i.u,i.temp, i.c1, s.server, s.hostname, h.codes,s.cpus
FROM `dc_servers` AS s
INNER JOIN `dc_hardware` AS h ON s.server = h.server
INNER JOIN `dc_systemusage` AS i ON s.server = i.server
WHERE i.id IN (SELECT max(dc_systemusage.id) FROM dc_systemusage GROUP BY dc_systemusage.server)
and check a great answer at https://stackoverflow.com/a/3800572/7174186
I am trying to create an SQL query, but when I left join an additional table (c) with conditions it reduces the number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.NNaziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.id
ORDER BY a.idArtikla ASC
Query below works great. I would like to keep the records I get, but I would like to JOIN another table. Once I do that it reduces number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal
WHERE c.status = 1
ORDER BY a.idArtikla ASC
How I can show all rows from first query and attach table radni_nalozi c with where condition that sums only quantity with status = 1?
try this
With Outer Join, if you put in where clause then it will be treated as inner join.
$stmt = "SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla, norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFTJOIN radni_nalozi c ON a.idArtikla = c.idArtikal and c.status = 1
ORDER BY a.idArtikla ASC";
You outer-join radni_nalozi. This means that in case there is no match, the original row is kept and all radni_nalozi columns are null. Then you limit your results by:
WHERE c.status = 1
This dismisses all outer-joined rows, because their status is null. You have turned your outer join into an inner join.
What you want instead is the condition to be part of the join clause:
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal AND c.status = 1
But as P.Salmon just pointed out, your query is invalid, because of a malformed aggregation. MySQL should raise a syntax error, but obviously you have not SET sql_mode = 'ONLY_FULL_GROUP_BY', which you should in order to have the DBMS prevent you from writing such invalid queries. With sum(c.kolicina) and no GROUP BY clause, you tell the DBMS to limit your results to a single row containing the sum. But what values for a.id, a.naziv, etc. is the DBMS supposed to show then?
It seems you want to join the radni_nalozi sums:
SELECT
ar.id,
ar.naziv as nazivOperacije,
a.Naziv as nazivArtikla,
a.sifra as sifraArtikla,
a.norma,
ar.idArtikla,
rn.sum_kolicina AS kolRadnogNaloga
FROM artikli_rad ar
JOIN artikli a ON a.id = ar.idArtikla
LEFT JOIN
(
SELECT idArtikal, SUM(kolicina) AS sum_kolicina
FROM radni_nalozi
WHERE status = 1
GROUP BY idArtikal
) rn ON rn.idArtikla = ar.idArtikal
ORDER BY ar.idArtikla ASC;
I have also used mnemonic alias names. names like a, b, and c don't make the query more readable as alias names are supposed to do, but make it less readable.
I also turned the outer join on artikli into an inner join, because there must be no artikli_rad row without a match in artikli in a properly set up database.
I'm having an issue updating a table with a select using multiple joins. I feel like everything is in place but I'm getting some syntax problems around the end, as commented below.
UPDATE ambition.ambition_totals a
INNER JOIN (SELECT
c.user AS UserID,
COUNT(*) AS dealers,
ROUND((al.NumberOfDealers / al.NumberOfDealerContacts) * 100 ,2) AS percent
FROM contact_events c
JOIN users u
ON c.user = u.id
JOIN dealers d
ON c.dealer_num = d.dealer_num
LEFT JOIN (
SELECT user_id, COUNT(*) AS NumberOfDealerContacts,
SUM(CASE WHEN ( d.next_call_date + INTERVAL 7 DAY) THEN 1 ELSE 0 END) AS NumberOfDealers
FROM attr_list AS al
JOIN dealers AS d ON d.csr = al.data
WHERE al.attr_id = 14
GROUP BY user_id)) as al
ON al.user_id = a.ext_id -- this is where I have a syntax error
SET a.dealers_contacted = al.dealers,
a.percent_up_to_date = al.percent;
As shown, I'm getting the data needed from these joins but I'm unable to update based on my ON clause in the final join. The select itself works apart from this, but I'm just trying to alter it to update a table.
I'm sure I'm just overlooking something in the syntax but I get an error that 'every derived table must have its own alias'.
UPDATE
Original working select that needs to be converted into the update:
SELECT
c.user AS UserID,
COUNT(*) AS Number_of_recorded_events,
ROUND((al.NumberOfDealers / al.NumberOfDealerContacts) * 100 ,2) AS Percentage_up_to_date
FROM contact_events c
JOIN users u
ON c.user = u.id
JOIN dealers d
ON c.dealer_num = d.dealer_num
LEFT JOIN (
SELECT user_id, COUNT(*) AS NumberOfDealerContacts,
SUM(CASE WHEN ( d.next_call_date + INTERVAL 7 DAY) THEN 1 ELSE 0 END) AS NumberOfDealers
FROM jackson_id.attr_list AS al
JOIN jfi_dealers.dealers AS d ON d.csr = al.data
WHERE al.attr_id = 14
GROUP BY user_id) AS al
ON al.user_id = c.user
GROUP BY UserID;
'every derived table must have its own alias'
This error is pretty clear. A derived table is when you put a subquery in a FROM clause or JOIN clause, which you do twice in your query.
Every time you do this, you must give each of these derived table subqueries an alias, so you can reference columns returned by the subquery.
Like:
SELECT t.foo FROM (SELECT foo FROM MyTable) AS t
This must be done for every such subquery. In your case, you have something like this form:
UPDATE a
INNER JOIN (
SELECT ... FROM c JOIN u JOIN d
LEFT JOIN (SELECT ... FROM al JOIN d ...)
) AS al
SET ...
You have one level of subquery, which you give the alias al.
But you don't give an alias for the innermost subquery, the one you did a LEFT JOIN on. That one needs an alias too.
P.S.: This question is actually a duplicate of What is the error "Every derived table must have its own alias" in MySQL? from 2009. I know Stack Overflow encourages us to close new questions as duplicates if there is already an old answer. But I also know the reality is that people tend not to search old posts much.
On the other hand, that old Stack Overflow post from 2009 is literally the first result when I google for the error string 'every derived table must have its own alias'.
I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;
I've created sqlfiddle to try and get my head around this http://sqlfiddle.com/#!2/21e72/1
In the query, I have put a max() on the compiled_date column but the recommendation column is still coming through incorrect - I'm assuming that a select statement will need to be inserted on line 3 somehow?
I've tried the examples provided by the commenters below but I think I just need to understand this from a basic query to begin with.
As others have pointed out, the issue is that some of the select columns are neither aggregated nor used in the group by clause. Most DBMSs won't allow this at all, but MySQL is a little relaxed on some of the standards...
So, you need to first find the max(compiled_date) for each case, then find the recommendation that goes with it.
select r.case_number, r.compiled_date, r.recommendation
from reporting r
join (
SELECT case_number, max(compiled_date) as lastDate
from reporting
group by case_number
) s on r.case_number=s.case_number
and r.compiled_date=s.lastDate
Thank you for providing sqlFiddle. But only reporting data is given. we highly appreciate if you give us sample data of whole tables.
Anyway, Could you try this?
SELECT
`case`.number,
staff.staff_name AS ``case` owner`,
client.client_name,
`case`.address,
x.mx_date,
report.recommendation
FROM
`case` INNER JOIN (
SELECT case_number, MAX(compiled_date) as mx_date
FROM report
GROUP BY case_number
) x ON x.case_number = `case`.number
INNER JOIN report ON x.case_number = report.case_number AND report.compiled_date = x.mx_date
INNER JOIN client ON `case`.client_number = client.client_number
INNER JOIN staff ON `case`.staff_number = staff.staff_number
WHERE
`case`.active = 1
AND staff.staff_name = 'bob'
ORDER BY
`case`.number ASC;
Check below query:
SELECT c.number, s.staff_name AS `case owner`, cl.client_name,
c.address, MAX(r.compiled_date), r.recommendation
FROM case c
INNER JOIN (SELECT r.case_number, r.compiled_date, r.recommendation
FROM report r ORDER BY r.case_number, r.compiled_date DESC
) r ON r.case_number = c.number
INNER JOIN client cl ON c.client_number = cl.client_number
INNER JOIN staff s ON c.staff_number = s.staff_number
WHERE c.active = 1 AND s.staff_name = 'bob'
GROUP BY c.number
ORDER BY c.number ASC
SELECT
case.number,
staff.staff_name AS `case owner`,
client.client_name,
case.address,
(select MAX(compiled_date)from report where case_number=case.number),
report.recommendation
FROM
case
INNER JOIN report ON report.case_number = case.number
INNER JOIN client ON case.client_number = client.client_number
INNER JOIN staff ON case.staff_number = staff.staff_number
WHERE
case.active = 1 AND
staff.staff_name = 'bob'
GROUP BY
case.number
ORDER BY
case.number ASC
try this