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