I need help writing a query to get some information but I am having trouble writing it.
[table_People]
int id
var name
[table_Tools]
int id
var name
[table_Activity1]
int person_id
int tool_id
date delivery_date
[table_Activity2]
int person_id
int tool_id
date installation_date
The query needs to return a list of all people and the name of the most recent tool they used in either activity 1 or 2 (the most recent activity that happened between the two).
SELECT
people.id AS personId,
people.name AS personName,
(
SELECT
tools.name AS toolName
FROM
activity1
JOIN
tools ON tools.id=activity1.tool_id
WHERE
activity1.id=people.id
UNION ALL
SELECT
tools.name AS toolName
FROM
activity2
JOIN
tools ON tools.id=activity2.tool_id
WHERE
activity2.id=people.id
ORDER BY
installationDate,deliveryDate
) AS toolName
FROM
people
ORDER BY
people.name
ASC
The problem I am having is that I can't sort by date (delivery or installation) as I get errors because they are different column names.
Using UNION in a subquery creates a derived temporary table. Columns that aren't selected are not in the result set, so you can't ORDER on a column that's not in the SELECT clause.
When using UNION, the first column name that is used in the SELECT clause is used in the result set (similar to an alias, though you could also use an alias).
Just be sure to name the column in the SELECT clause.
You also need a LIMIT clause to restrict the subquery to a single row:
SELECT
people.id AS personId,
people.name AS personName,
(
SELECT
tools.name AS toolName, delivery_date
FROM
activity1
JOIN
tools ON tools.id=activity1.tool_id
WHERE
activity1.id=people.id
UNION ALL
SELECT
tools.name AS toolName, installation_date
FROM
activity2
JOIN
tools ON tools.id=activity2.tool_id
WHERE
activity2.id=people.id
ORDER BY
deliveryDate
LIMIT 1
) AS toolName
FROM
people
ORDER BY
people.name
ASC
Here's a more simple example to illustrate the issue:
SELECT fish FROM sea
UNION
SELECT dog FROM land
ORDER BY fish
Is the same as:
SELECT fish AS animal FROM sea
UNION
SELECT dog AS animal FROM land
ORDER BY animal
The results are put into a derived temporary table, and you can name the columns whatever you want, but the first name that you use sticks.
My solution puts the unions together in a subquery and then orders by them. You only want the first row, so you need a limit clause (or rownum = 1 in Oracle or top 1 in MSSQL):
SELECT people.id AS personId,
people.name AS personName,
(SELECT toolname
FROM ((SELECT tools.name AS toolName, delivery_date as thedate
FROM activity1 a
WHERE a.PersonId = people.id
) union all
(SELECT tools.name AS toolName, installation_date as thedate
FROM activity2 a
WHERE a.PersonId = people.id
)
) a join
tools t
on a.toolsid = t.toolsid
order by 2 desc
limit 1
) AS toolName
FROM people
ORDER BY people.name ASC
To simplify the query, I also removed the innermost join to tools.
you cannot sort columns without specifying them after your select.
For Example:
select name from people order by name
select a1.delivery_date, t.name from activity1 a1, tools t
order by a1.delivery_date,t.name
All selected columns for the projection MUST be defined in the order by definition as well. In your example both select statements are just taking tools.name as toolname but you want to sort by other columns.
MySQL version (possibly you can get more compact code from it):
Select * from
(
Select toolName, person_id, Max(TargetDate) as MaxDate
From
(
SELECT tools.name AS toolName, activity1.person_id, activity1.delivery_date as targetDate
FROM
activity1
JOIN
tools ON tools.id=activity1.tool_id
UNION ALL
SELECT
tools.name AS toolName, activity2.person_id, activity2.installation_date as TargetDate
FROM
activity2
JOIN
tools ON tools.id=activity2.tool_id
)
Group by toolName, person_id
) preselect
join
(
Select toolName, person_id, Max(TargetDate)
From
(
SELECT tools.name AS toolName, activity1.person_id, activity1.delivery_date as targetDate
FROM
activity1
JOIN
tools ON tools.id=activity1.tool_id
UNION ALL
SELECT
tools.name AS toolName, activity2.person_id, activity2.installation_date as TargetDate
FROM
activity2
JOIN
tools ON tools.id=activity2.tool_id
)) result on result.toolName = preselect.toolName and result.person_id = preselect.person_id and result.TargetDate = preselect.MaxDate
Do
SELECT
people.id AS personId,
people.name AS personName,
IF (
(SELECT
deliveryDate AS dDate
FROM
activity1
WHERE
person_id=personId) --assuming you have only one row returned here, else limit by some condition
>
(SELECT
installationDate AS iDate
FROM
activity2
WHERE
person_id=personId) --assuming you have only one row returned here, else limit by some condition
, (SELECT
tools.name AS toolName
FROM
activity1
JOIN
tools ON tools.id=activity1.tool_id
WHERE
activity1.person_id=personId)
, (SELECT
tools.name AS toolName
FROM
activity2
JOIN
tools ON tools.id=activity2.tool_id
WHERE
activity2.person_id=personId)
) AS toolName
FROM
people
ORDER BY
people.name
ASC
This query assumes there is only record per person in activity tables. If there are more you need to limit your select result set based on sum maximum condition or so which only u know.
SELECT
p.id AS person_id
, p.name AS person_name
, CASE WHEN COALESCE(a1.delivery_date, '1000-01-01')
> COALESCE(a2.installation_date, '1000-01-01')
THEN t1.name
ELSE t2.name
END AS tool_name
FROM
People AS p
LEFT JOIN
Activity1 AS a1
ON (a1.tool_id, a1.delivery_date) =
( SELECT tool_id, delivery_date
FROM Activity1 AS a
WHERE a.person_id = p.id
ORDER BY delivery_date DESC
LIMIT 1
)
LEFT JOIN
Tools AS t1
ON t1.id = a1.tool_id
LEFT JOIN
Activity2 AS a2
ON (a2.tool_id, a2.installation_date) =
( SELECT tool_id, installation_date
FROM Activity2 AS a
WHERE a.person_id = p.id
ORDER BY installation_date DESC
LIMIT 1
)
LEFT JOIN
Tools AS t2
ON t2.id = a2.tool_id
select people.id as person_id,people.name as person_name,tools.name as toolsname
from table_people people left join
(
select
case when installation_date>delivery_date then act2.tool_id
else act1.tool_id end as recent_most_tool_id,
case when installation_date>delivery_date then act2.person_id
else act1.person_id end as recent_most_person_id
from table_activity1 act1 inner join table_activity2 act2
on act1.person_id=act2.person_id)X
on people.id=X.recent_most_person_id
inner join table_tools tools
on tools.id=X.recent_most_tool_id
Related
I know that the question name is kinda confusing, but what I want to do is (if possible)the following: In the following MYSQL query, I can get all phone numbers from the first 15 people, what I need is that with this query, if the person has more than 3 phone numbers, only return the first 3.
SELECT distinct(pp.PhoneNumber)
FROM person p
INNER JOIN personPhone pp ON p.personId = pp.personId
WHERE !ISNULL(pp.PhoneNumber)
GROUP BY p.PersonId, pp.PhoneNumber
ORDER BY p.personId ASC LIMIT 0,15;
I have tried making a subquery on the SELECT, inside the distinct statement, but without any success
EDIT: Sorry I forgot to add the version of MySQL that I'm using, it is 5.7.24
You don't say what version of MySQL you have. If you have MySQL 8.x you can do:
select *
from (
select
personId, PhoneNumber,
row_number() over(partition by personId order by PhoneNumber) as rn
from (
SELECT distinct p.personId, pp.PhoneNumber
FROM person p
INNER JOIN personPhone pp ON p.personId = pp.personId
) x
) y
where rn <= 3
Another approach would be to use GROUP_CONCAT to get all the phone numbers per person and then use SUBSTRING_INDEX to make sure you fetch only up to 3.
SELECT p.PersonId ID, SUBSTRING_INDEX(GROUP_CONCAT(DISTINCT(pp.PhoneNumber)), ',', 3) Phone
FROM person p
INNER JOIN personPhone pp ON p.PersonId = pp.PersonId
WHERE pp.PhoneNumber IS NOT NULL
GROUP BY p.PersonId
ORDER BY p.PersonId ASC LIMIT 15;
Your query does not get all phone numbers from the first 15 people. It gets 15 phone numbers, which is probably for fewer than 15 people.
To limit to 15 people, use a subquery. Then you can limit to three numbers per person:
SELECT distinct(pp.PhoneNumber)
FROM (SELECT p.*
FROM person p
ORDER BY p.personId
LIMIT 15
) p15 INNER JOIN
personPhone pp
ON p.personId = pp.personId
WHERE pp.PhoneNumber IS NOT NULL AND
pp.PhoneNumber <= COALESCE( (SELECT pp2.PhoneNumber
FROM personPhone pp2
WHERE pp2.PersonId = pp.PersonId
ORDER BY pp2.PhoneNumber
OFFSET 2 LIMIT 1
), pp.PhoneNumber)
This query calculates the columns free,plus,score and total based on the COUNT of columns in subquery.
SELECT movie_title,movie_id,MAX(x.free_cnt) as free, MAX(x.plus_cnt) as plus,
(MAX(x.free_cnt) + (MAX(x.plus_cnt)*3)) AS score, (MAX(x.free_cnt) + MAX(x.plus_cnt)) AS total
FROM (
SELECT b.id as movie_id, b.movie_title as movie_title, COUNT(*) AS free_cnt, 0 as plus_cnt
FROM subtitles_request a1
LEFT JOIN movies b on a1.movie_id=b.id
JOIN users c on c.email=a1.email
WHERE c.subsc_status='0'
GROUP BY b.movie_title
UNION ALL
SELECT d.id as movie_id, d.movie_title as movie_title, 0 as free_cnt, COUNT(*) AS plus_cnt
FROM subtitles_request a2
LEFT JOIN movies d on a2.movie_id=d.id
JOIN users e on e.email=a2.email
WHERE e.subsc_status='1'
GROUP BY d.movie_title
) AS x
GROUP BY movie_title
ORDER BY total DESC
LIMIT 10
It is slow performing and i'm wondering is there anyway i can simplify or change the query to speed up performance. I can't calculate the free,plus,score ,total columns outside of query due to being able to order by. Also i may incorporate date.
Anyway to simplify this query?
Try this:
SELECT b.movie_title, x.movie_id, MAX( x.free_cnt ) AS free, MAX( x.plus_cnt ) AS plus,
( MAX( x.free_cnt ) + ( MAX( x.plus_cnt ) * 3 ) ) AS score, ( MAX( x.free_cnt ) + MAX( x.plus_cnt ) ) AS total
FROM ( SELECT a.movie_id,
SUM( IF( c.subsc_status = '0', 1, 0 ) ) AS free_cnt,
SUM( IF( c.subsc_status = '1', 1, 0 ) ) AS plus_cnt
FROM subtitles_request a1
JOIN users c on c.email=a1.email
WHERE c.subsc_status in ('0','1')
GROUP BY a.movie_id
) AS x
LEFT JOIN movies b on x.movie_id = b.id
GROUP BY movie_title, movie_id
ORDER BY total DESC
LIMIT 10
Maybe I've simplified a bit too much. Moreover, I'm not used to grouping on only some of the non-aggregate fields, hence I added movie_id to what is being grouped by and thus changing your query a bit (if two films had the same name, but different ID, then only one of the id's would be returned in your original query, but I guess (being a MySQL newbie, I really don't know) the counts would be for both of them taken together).
HTH,
Set
Well, I have check your the subquery:
SELECT b.id as movie_id, b.movie_title as movie_title, COUNT(*) AS free_cnt, 0 as plus_cnt
FROM subtitles_request a1
LEFT JOIN movies b on a1.movie_id=b.id
JOIN users c on c.email=a1.email
WHERE c.subsc_status='0'
GROUP BY b.movie_title
UNION ALL
SELECT d.id as movie_id, d.movie_title as movie_title, 0 as free_cnt, COUNT(*) AS plus_cnt
FROM subtitles_request a2
LEFT JOIN movies d on a2.movie_id=d.id
JOIN users e on e.email=a2.email
WHERE e.subsc_status='1'
GROUP BY d.movie_title
The statement beside "UNION ALL" can be replaced with one statement with condition at c.subsc_status IN('0','1'). And you can try to use "CASE WHEN" statement at 0 as free_cnt, COUNT(*) AS plus_cnt, just like IFNULL((CASE WHEN e.subsc_status='1' THEN COUNT(*)),0) as free_cnt. It's not a complicated sql statement, I don't think it will take too much time to query. Is there too many datas?
As a matter of fact, I'm also a newer, but I just have some experence about it. Please forgive me if it doesn't work.
Hi I have this item about insert if not exists here. One of the things I want to know about is if I want to get the latest items from CompResults by using order by ResultDate, to be inserted to Competitors table, how should I do it?
INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
NOT EXISTS (SELECT * FROM Competitors c
WHERE cr.Name = c.cName) ORDER BY cr.ResultsDate DESC
An error happens: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
Hi you have to use order by fields in select statement I think You are using sql server
so You can use sub query
INSERT Competitors (cName)
select Name
from (
SELECT cr.Name,max(cr.ResultDate)
FROM CompResults cr
WHERE NOT EXISTS (SELECT * FROM Competitors c
WHERE cr.Name = c.cName) group BY cr.name) as t order by ResultDate
Use Row_Number to get the latest record for each Item
Insert into Competitors(col1,col2..)
Select col1,col2,..
(
Select row_number()Over(partition by Name order by ResultDate desc) Rn, *
From CompResults cr
NOT EXISTS (SELECT 1 FROM Competitors c
WHERE cr.Name = c.cName)
) a
Where Rn = 1
Also you can as the below:
INSERT Competitors (cName)
SELECT
A.Name
FROM
CompResults A INNER JOIN
(
SELECT
CR.Name,
MAX(CR.ResultsDate) MaxResultsDate
FROM
CompResults CR
) B ON A.Name = B.Name AND A.ResultsDate = B.MaxResultsDate
WHERE
NOT EXISTS (SELECT 1 FROM Competitors c
WHERE c.cName = A.Name)
I wrote the following query to return the the records with the latest date.
select fs.company_id, max(fs.create_dt) as latestcreatedate
from field_sale fs
group by fs.company_id
order by fs.company_id
The query all works fine. But I need to retrieve the record with all related columns attached to it. Such as, id, title, desc and etc.
How can I retrieve the records with its corresponding columns?
Couple ways of doing so :
-- 1.
SELECT a.*
FROM field_sale a
INNER JOIN
(
select fs.company_id, max(fs.create_dt) as latestcreatedate
from field_sale fs
group by fs.company_id
)b
ON b.company_id = a.company_id AND b.latestcreatedate = a.create_dt
order by a.company_id;
-- 2.
SELECT b.* FROM
(
SELECT a.* , ROW_NUMBER()
OVER (PARTITION BY a.company_id ORDER BY a.create_dt DESC)
AS rn
FROM field_sale a
)b WHERE b.rn = 1
ORDER BY company_id
WITH t AS (
SELECT fs.company_id,
fs.create_dt AS latestcreatedate,
id,
title,
etc,
ROW_NUMBER() OVER ( PARTITION BY fs.company_id ORDER BY fs.create_dt DESC ) AS rowNum
FROM field_sale fs
)
SELECT t.company_id,
t.latestcreatedate,
t.id,
t.title,
t.etc
FROM t
WHERE t.rowNum = 1
ORDER BY t.company_id
I'm not able to use JOINS and group-by together:
I searched for this...but I didn't find the solution..Problem is as below:
I have two tables, first is main_table with fields eid, name, status
Second table is followups with fields fid, eid, last_date, remarks, and next_date
Second table is used to store followup details of clients (client details stored in main_table) and i want to get last followup record for each client with selected date period (from_date to to_date) and sort by next_date in descending order.
i used the below query but not working
SELECT *
FROM main_table as MT
JOIN followups as MF on MT.eid=MF.eid
WHERE MT.status='open' AND MF.NDate<='2012-12-07'
GROUP BY MF.eid
ORDER BY MF.next_date DESC
thanks in advance...guys
Try This
SELECT * FROM main_table AS MT
LEFT JOIN (SELECT * FROM (SELECT * FROM followups ORDER BY next_date DESC) AS A GROUP BY eid) AS MF ON MT.eid=MF.eid
WHERE MT.status='open'
AND MF.NDate<='2012-12-07';
You can try something like this:
select m.eid,
m.name,
m.status,
f1.last_date,
f1.remarks,
f1.next_date
from maintable m
left join
(
select max(last_date) maxLast, eid
from followups
where last_date between from_date and to_date
group by eid
) f
on m.eid = f.eid
left join followups f1
on f.maxLast = f1.last_date
and f.eid = f1.eid
where m.status='open'
and f1.next_date<='2012-12-07'
order by f1.next_date desc
Try this:
SELECT * FROM main_table AS MT
LEFT JOIN (SELECT * FROM (SELECT * FROM followups WHERE NDate<='2012-12-07' ORDER BY next_date DESC) AS A GROUP BY eid) AS MF ON MT.eid=MF.eid
WHERE MT.status='open';