This question already has answers here:
What is the error "Every derived table must have its own alias" in MySQL?
(4 answers)
Closed 3 months ago.
on mysql I see this error but can fix it. Anyone can help please?
select * from
(
(select a.* from sessions as a)
join
(
select b.customer_id, min(b.timestamp),
b.marketing_source as first_touch_source,
b.marketing_medium as first_touch_medium
from sessions as b
group by b.customer_id
) on a.customer_id = b=customer_id
) as T
I believe your query should read
select *
from (
select a.*
from sessions as a
join
(select b.customer_id, min(b.timestamp), b.marketing_source as first_touch_source, b.marketing_medium as first_touch_medium
from sessions as b
group by b.customer_id
) c USING (customer_id) # or c.customer_id = a.customer_id
) as T
If you are just trying to bring in the minimum date column you could try two approaches, second one will work if your version supports windows function
subqueries are always a mess so I would suggest cte's if supported
with main as (
select
b.customer_id,
b.marketing_source as first_touch_source,
b.marketing_medium as first_touch_medium,
min(b.timestamp) as min_time,
from sessions as b
group by b.customer_id
)
select
s.*,
main.min_time
from sessions as s
inner join main
on s.customer_id = main.customer_id
select
*,
min(timestamp)
over(partition by customer_id,marketing_medium, marketing_source) as min_date_per_cust_med_source
from sessions
Some DBMS require you to name all derived tables. Your query (I removed the unnessesary derived table T):
select *
from (select a.* from sessions as a)
join (select b.customer_id, min(b.timestamp)
, b.marketing_source as first_touch_source
, b.marketing_medium as first_touch_medium
from sessions as b
group by b.customer_id
)
on a.customer_id = b=customer_id
can be changed to:
select *
from (select a.* from sessions as a) AS c
join (select b.customer_id, min(b.timestamp)
, b.marketing_source as first_touch_source
, b.marketing_medium as first_touch_medium
from sessions as b
group by b.customer_id
) AS d
on c.customer_id = d.customer_id
To avoid confusion, you should choose another alias at the outer level, despite that the inner alias is not visible there.
Side note: The derived table d may or may not be valid SQL. It is not allowed in SQL92, but it is allowed in SQL99 if marketing_* is functionally dependent of customer_id.
You can further simplify it as:
select *
from sessions AS c
join (select b.customer_id, min(b.timestamp) as ts
, b.marketing_source as first_touch_source
, b.marketing_medium as first_touch_medium
from sessions as b
group by b.customer_id
) AS d
on c.customer_id = d.customer_id
I assume you meant to also join with c.timestamp = d.ts. If that is the case and you are on a recent version of MySQL (8+) you can use a window function instead of a self join
select customer_id, ...
from (
select b.customer_id
, b.marketing_source
, b.marketing_medium
, row_number() over (partition by customer_id
order by b.timestamp) as rn
from sessions as b
) as T
where rn = 1
Related
I have good working sql query but I need to select also atribute from table advert. I tried with inner join but it wasn't successful. So this query is ok but I need to select one atribute from table advert.
SELECT D.* FROM details
WHERE (D.name LIKE ?) AND (D.id_advert IN(
SELECT A.id
FROM advert A
WHERE A.status=1 and duration >= CURDATE()
ORDER BY duration DESC ))
You can change the in (subquery ) in a proper inner join and the is simple use the columns form table A
SELECT D.* , A.*
FROM details
INNER JOIN advert A ON D.id_advert = A.id
AND A.status=1
AND duration >= CURDATE()
WHERE D.name LIKE ?
SELECT *
FROM details D
INNER JOIN advert A ON D.id_advert = A.id
INNER JOIN place P ON A.id_place = P.id
WHERE (D.NAME LIKE ?)
AND ( D.id_advert IN (
SELECT A.id
FROM advert A
WHERE A.STATUS = 1 AND duration >= CURDATE()
ORDER BY duration DESC
)
)
Here "?" is for search key. This query work perfect.
I have 3 tables concerning complains. The first table consists of the complain information itself, 2nd one is the complain_review with status_id, and the 3rd is the status_id table consisting status information. I'm trying to select the complain_desc from complain and latest status_id from complain_review (sort by date desc) and couple that with complain_status information.
This is what I've tried (no success so far):
SELECT c1.complain_desc, c2.status_id, c2.name as statusDesc from complain c1
left join
(SELECT c3.status_id, c4.name, c3.complain_id FROM complain_review c3
inner join complain_status c4 on c4.id=c3.status_id ORDER by c3.date DESC) c2
on c2.complain_id=c1.id
this is the updated example provided by #maheshiv
.. I've searched through the site but I don't exactly know what keyword to search concerning this matter :(
Edit: I've build a schema at http://sqlfiddle.com/#!9/d86a7a/2 so perhaps somebody could give take a better look at the tables
Edit: Perhaps this would be the closest as I could get .. and working!
SELECT c.complain_desc, cr1.status_id, cs.name
FROM complain c
INNER JOIN complain_review cr1 ON c.id=cr1.complain_id
INNER JOIN complain_status cs ON cs.id=cr1.status_id
WHERE cr1.date = (SELECT MAX(cr2.date) FROM complain_review cr2
WHERE cr1.complain_id=cr2.complain_id)
I'm trying to select the complain_desc from complain and latest status_id from complain_review (sort by date desc) and couple that with complain_status information.
This is a very common question on Stack Overflow. You can follow the greatest-n-per-group to find many solutions.
Here's a solution using your example:
SELECT c.complain_desc, latest_cr.status_id, cs.name AS status_desc
FROM complain AS c
INNER JOIN (
SELECT complain_id, status_id
FROM (
SELECT cr.complain_id, cr.status_id,
IF(#cgroup=cr.complain_id, #rownum:=#rownum+1, 1) AS rownum,
(#cgroup:=cr.complain_id)
FROM (SELECT #cgroup:=0, #rownum:=1) AS _init
CROSS JOIN complain_review AS cr
ORDER BY cr.complain_id DESC, cr.date DESC
) AS n
WHERE n.rownum = 1
) AS latest_cr
ON c.id=latest_cr.complain_id
INNER JOIN complain_status AS cs
ON cs.id = latest_cr.status_id;
Here's a different solution using no subqueries:
SELECT c.complain_desc, cr1.status_id, cs.name AS status_desc
FROM complain AS c
INNER JOIN complain_review AS cr1
ON cr1.complain_id = c.id
LEFT OUTER JOIN complain_review AS cr2
ON cr2.complain_id = c.id AND (cr2.date > cr1.date OR cr2.date = cr1.date AND cr2.id > cr1.id)
INNER JOIN complain_status AS cs
ON cs.id = cr1.status_id
WHERE cr2.id IS NULL;
I think you may need this query,
I believe max status_id is the latest status for complaint. As per http://sqlfiddle.com/#!9/d86a7a/15
select c1.complain_desc, c2.status_id, c3.name from complain c1 inner join (select complain_id, max(status_id) from complain_review group by complain_id) c2 on c1.id=c2.complain_id inner join complain_status c3 on c3.id=c2.status_id;
Is it possible to sum on cols if inner join is true and sum on another cols if another inner join true? i.e.
SELECT t1.debit
, t2.credit
, t1.ID
, t2.ID
FROM
( SELECT SUM(booking_value) debit
, gl_acct.id_fin_gl_acct ID
FROM bookings
JOIN gl_acct
ON (CONCAT('1',gl_acct.id_fin_gl_acct) = bookings.id_debit_account)
) t1
JOIN
( SELECT SUM(booking_value) credit
, gl_acct.id_fin_gl_acct ID
FROM bookings
JOIN gl_acct
ON (CONCAT('1',gl_acct.id_fin_gl_acct)=bookings.id_credit_account)
) t2
ON (t1.ID = t2.ID)
GROUP
BY t1.ID
Please explain your answer.
An example of conditional aggregation.
SELECT t.id_account
, SUM(IF(t.cr_or_db='cr',t.tot_booking_value,0)) AS `tot_credit`
, SUM(IF(t.cr_or_db='db',t.tot_booking_value,0)) AS `tot_debit`
FROM (
SELECT 'cr' AS `cr_or_db`
, c.id_credit_account AS `id_account`
, SUM(c.booking_value) AS `tot_booking_value`
FROM bookings c
GROUP BY c.id_credit_account
UNION ALL
SELECT 'db' AS `cr_or_db`
, d.id_debit_account AS `id_account`
, SUM(d.booking_value) AS `tot_booking_value`
FROM bookings d
GROUP BY d.id_debit_account
) t
GROUP
BY t.id_account
The inline view t gets us total credits for each id_credit_account and total debits for each id_debit_account.
The original query joins both of those id_ columns to the same account table, so we're assuming those are both foreign keys to that other table, which means they are the same datatype...
A join operation would only be required if there is some reason we need to include the gl_acct table. Assuming that id_fin_gl_acct is UNIQUE in gl_acct... we could add the join operation before the GROUP BY clause.
...
) t
JOIN gl_acct g
ON CONCAT('1',g.id_fin_gl_acct) = t.id_account
GROUP
BY t.id_account
I have this code:
SELECT a.id, a.to_id, a.from_id, a.seen, a.date, a.message
FROM `Chat_messages` a
INNER JOIN (
SELECT MAX( `id` ) AS id
FROM `Chat_messages` AS `alt`
WHERE `alt`.`to_id` =7
OR `alt`.`from_id` =7
GROUP BY `to_id` , `from_id`
)b ON a.id = b.id
returning:
So, I want to get conversations (sent and received messages) of an user and latest message of it.
Latest message works ok, but the problem is that I get 2 rows from messages received (#1 and #2) and 2 rows from messages sent (#3 and #4), but I only need 2 results, because there are 2 conversations.
The best way to pick out the row that holds the latest sent message and the row that holds the latest received message respectively, is using the row_number() window function. Unfortunately, MySql does not support window functions, so I think it's best to use two nested SELECT's:
SELECT z.id, max(z.to_id), max(z.from_id), max(z.seen), max(z.date), max(z.message)
FROM chat_messages z
LEFT JOIN
(SELECT x.from_id, max(date) date
FROM chat_messages x
GROUP BY x.from_id) f
ON z.from_id = f.from_id AND z.date = f.date
LEFT JOIN
(SELECT y.to_id, max(date) date
FROM chat_messages y
GROUP BY y.to_id) t
ON z.to_id = t.to_id AND z.date = t.date
GROUP BY z.id
I do not recommend using max on ID's if you care about correctness in the long run.
You can group by least(to_id, from_id), greatest(to_id, from_id) to make sure conversions between 2 people are merged:
SELECT a.id, a.to_id, a.from_id, a.seen, a.date, a.message
FROM `Chat_messages` a
INNER JOIN (
SELECT MAX( `id` ) AS id
FROM `Chat_messages` AS `alt`
WHERE `alt`.`to_id` =7
OR `alt`.`from_id` =7
GROUP BY least(`to_id` , `from_id`), greatest(`to_id` , `from_id`)
)b ON a.id = b.id
I have this query
SELECT
s.account_number,
a.id AS 'ASPIRION ID',
a.patient_first_name,
a.patient_last_name,
s.admission_date,
s.total_charge,
astat.name AS 'STATUS',
astat.definition,
latest_note.content AS 'LAST NOTE',
a.insurance_company
FROM
accounts a
INNER JOIN
services s ON a.id = s.account_id
INNER JOIN
facilities f ON f.id = a.facility_id
INNER JOIN
account_statuses astat ON astat.id = a.account_status_id
INNER JOIN
(SELECT
account_id, MAX(content) content, MAX(created)
FROM
notes
GROUP BY account_id) latest_note ON latest_note.account_id = a.id
WHERE
a.facility_id = 56
My problem comes from
(SELECT
account_id, MAX(content) content, MAX(created)
FROM
notes
GROUP BY account_id)
Content is a varchar field and I am needed to get the most recent record. I now understand that MAX will not work on a varchar field the way that I want it. I am not sure how to be able to get the corresponding content with the MAX id and group that by account id on in this join.
What would be the best way to do this?
My notes table looks like this...
id account_id content created
1 1 This is a test 2011-03-16 02:06:40
2 1 More test 2012-03-16 02:06:40
Here are two choices. If your content is not very long and don't have funky characters, you can use the substring_index()/group_concat() trick:
(SELECT account_id,
SUBSTRING_INDEX(GROUP_CONCAT(content ORDER BY created desc SEPARATOR '|'
), 1, '|') as content
FROM notes
GROUP BY account_id
) latest_note
ON latest_note.account_id = a.id
Given the names of the columns and tables, that is likely not to work. Then you need an additional join or a correlated subquery in the from clause. I think that might be easiest in this case:
select . . .,
(select n.content
from notes n
where n.account_id = a.id
order by created desc
limit 1
) as latest_note
from . . .
The advantage to this method is that it only gets the notes for the rows you need. And, you don't need a left join to keep all the rows. For performance, you want an index on notes(account_id, created).
SELECT
s.account_number,
a.id AS 'ASPIRION ID',
a.patient_first_name,
a.patient_last_name,
s.admission_date,
s.total_charge,
astat.name AS 'STATUS',
astat.definition,
latest_note.content AS 'LAST NOTE',
a.insurance_company
FROM
accounts a
INNER JOIN services s ON a.id = s.account_id
INNER JOIN facilities f ON f.id = a.facility_id
INNER JOIN account_statuses astat ON astat.id = a.account_status_id
INNER JOIN
(SELECT account_id, MAX(created) mxcreated
FROM notes GROUP BY account_id) latest_note ON latest_note.account_id = a.id and
latest_note.mxcreated = --datetime column from any of the other tables being used
WHERE a.facility_id = 56
You have to join on the max(created) which would give the latest content.
Or you can change the query to
SELECT account_id, content, MAX(created) mxcreated
FROM notes GROUP BY account_id
as mysql allows you even if you don't include all non-aggregated columns in group by clause. However, unless you join on the max date you wouldn't get the correct results.
The last created record is the one for which does not exist a newer one. Hence:
SELECT
s.account_number,
a.id AS "ASPIRION ID",
a.patient_first_name,
a.patient_last_name,
s.admission_date,
s.total_charge,
astat.name AS "STATUS",
astat.definition,
latest_note.content AS "LAST NOTE",
a.insurance_company
FROM accounts a
INNER JOIN services s ON a.id = s.account_id
INNER JOIN facilities f ON f.id = a.facility_id
INNER JOIN account_statuses astat ON astat.id = a.account_status_id
INNER JOIN
(
SELECT account_id, content
FROM notes
WHERE NOT EXISTS
(
SELECT *
FROM notes newer
WHERE newer.account_id = notes.account_id
AND newer.created > notes.created
)
) latest_note ON latest_note.account_id = a.id
WHERE a.facility_id = 56;