writing a sql query in MySQL with subquery on the same table - sql

I have a table svn1:
id | date | startdate
23 2002-12-04 2000-11-11
23 2004-08-19 2005-09-10
23 2002-09-09 2004-08-23
select id,startdate from svn1 where startdate>=(select max(date) from svn1 where id=svn1.id);
Now the problem is how do I let know the subquery to match id with the id in the outer query. Obviously id=svn1.id wont work. Thanks!
If you have the time to read more:
This really is a simplified version of asking what I really am trying to do here. my actual query is something like this
select
id, count(distinct archdetails.compname)
from
svn1,svn3,archdetails
where
svn1.name='ant'
and svn3.name='ant'
and archdetails.name='ant'
and type='Bug'
and svn1.revno=svn3.revno
and svn3.compname=archdetails.compname
and
(
(startdate>=sdate and startdate<=edate)
or
(
sdate<=(select max(date) from svn1 where type='Bug' and id=svn1.id)
and
edate>=(select max(date) from svn1 where type='Bug' and id=svn1.id)
)
or
(
sdate>=startdate
and
edate<=(select max(date) from svn1 where type='Bug' and id=svn1.id)
)
)
group by id LIMIT 0,40;
As you notice select max(date) from svn1 where type='Bug' and id=svn1.id has to be calculated many times.
Can I just calculate this once and store it using AS and then use that variable later. Main problem is to correct id=svn1.id so as to correctly equate it to the id in the outer table.

I'm not sure you can eliminate the repetition of the subquery, but the subquery can reference the main query if you use a table alias, as in the following:
select id,
count(distinct archdetails.compname)
from svn1 s1,
svn3 s3,
archdetails a
where s1.name='ant' and
s3.name='ant' and
a.name='ant' and
type='Bug' and
s1.revno=s3.revno and
s3.compname = a.compname and
( (startdate >= sdate and startdate<=edate) or
(sdate <= (select max(date)
from svn1
where type='Bug' and
id=s1.id and
edate>=(select max(date)
from svn1
where type='Bug' and
id=s1.id)) or
(sdate >= startdate and edate<=(select max(date)
from svn1
where type='Bug' and
id=s1.id)) )
group by id LIMIT 0,40;
Share and enjoy.

You should be able to left join to a sub-select so you only run the query once. Then you can do a join condition to pull out the maximum for the ID on each record as shown below:
SELECT id,
COUNT(DISTINCT archdetails.compname)
FROM svn1,
svn3,
archdetails
LEFT JOIN (
SELECT id, MAX(date) AS MaximumDate
FROM svn1
WHERE TYPE = 'Bug'
GROUP BY id
) AS MaxDate ON MaxDate.id = svn1.id
WHERE svn1.name = 'ant'
AND svn3.name = 'ant'
AND archdetails.name = 'ant'
AND TYPE = 'Bug'
AND svn1.revno = svn3.revno
AND svn3.compname = archdetails.compname
AND (
(startdate >= sdate AND startdate <= edate)
OR (
sdate <= MaxDate.MaximumDate
AND edate >= MaxDate.MaximumDate
)
OR (
sdate >= startdate
AND edate <= MaxDate.MaximumDate
)
)
GROUP BY
id LIMIT 0,
40;

Try using alias, something like this should work:
select s.id,s.startdate from svn1.s where s.startdate>=(select max(date) from svn1.s2 where s.id=s2.id);

Related

Select only observations with a date more recent than the 30/6/2021 (dd/mm/yyyy)

I have the following code:
Select Tbl.Fromdate, Tbl.Por, Tbl.Porname, Tbl.Bmref3
From(
Select
To_Char(P.Fromdate, 'dd-mm-yyyy') As Fromdate, P.Por, P.Porname, W.Bmref3,
, RANK() OVER (PARTITION BY P.Por ORDER BY P.fromdate DESC) AS rank
From Tmsdat.Climandatecomps W
Inner Join Tmsdat.Portfolios P On (W.Porik = P.Porik)
Where 1=1
) Tbl
Where 1=1
And Tbl.Rank = 1
;
However, I wish to select only the observations that have a Fromdate more recent than the June 30, 2021. I tried to add Tbl.Fromdate> '30-06-2021' to the WHERE clause, but I did not receive the desired results.
Do you have any suggestions?
Thank you in advance.
Best regards,
You would put the condition in the inner query:
Select To_Char(P.Fromdate, 'dd-mm-yyyy') As Fromdate, P.Por, P.Porname, W.Bmref3,
RANK() OVER (PARTITION BY P.Por ORDER BY P.fromdate DESC) AS rank
From Tmsdat.Climandatecomps W inner join
Tmsdat.Portfolios P
On (W.Porik = P.Porik)
Where p.FromDate > date '2021-06-30'

SQL Select min(date) group by with corresponding row

There are some subsets of a parent set with release date. And I want the earliest release date with the subset's no, but in a group base.
I try this:
SELECT Sub_No,Parent_No,MIN(DATE)
FROM mytable
WHERE other_CODE IS NULL
GROUP BY Sub_No,Parent_No
ORDER BY Parent_No DESC
but I get this: but I only want the most earliest released one as the 2015-02-12's Sub_No and Parent_No.
Any help will be highly appreciated!
Parent_No (No column name) Sub_No
07 2015-02-12 90
07 2015-11-03 88
07 2017-02-06 59
You can use the NOT EXISTS as follows:
SELECT Sub_No,Parent_No,DATE
FROM mytable t
where other_CODE IS NULL
and not exists (select 1 from mytable tt
where tt.Parent_No = t.Parent_No and tt.date < t.date
and tt.other_CODE is null)
If your database supports analytical function then you can use it as follows:
select Sub_No,Parent_No, DATE from
(SELECT Sub_No,Parent_No, DATE,
row_number() over (partition by parent_no order by date) as rn
FROM mytable
WHERE other_CODE IS NULL) t
where rn = 1
Only if Parent_No and DATE are unique key of mytable, then you can do the trick like this:
SELECT * FROM mytable
INNER JOIN
(
SELECT Parent_No,MIN(DATE) as MinDate
FROM mytable
WHERE other_CODE IS NULL
GROUP BY Parent_No
) AS Filter
on mytable.Parent_No = Filter.Parent_No and mytable.DATE = Filter.MinDate

SQL - One Table with Two Date Columns. Count and Join

I have a table (vOKPI_Tickets) that has the following columns:
|CreationDate | CompletionDate|
I'd like to get a count on each of those columns, and group them by date. It should look something like this when complete:
| Date | Count-Created | Count-Completed |
I can get each of the counts individually, by doing something like this:
SELECT COUNT(TicketId)
FROM vOKPI_Tickets
GROUP BY CreationDate
and
SELECT COUNT(TicketId)
FROM vOKPI_Tickets
GROUP BY CreationDate
How can I combine the output into one table? I should also note that this will become a View.
Thanks in advance
Simple generic approach:
select
coalesce(crte.creationdate, cmpl.CompletionDate) as theDate,
crte.cnt as created,
cmpl.cnt as completed
from
(select creationdate, count (*) as cnt from vOKPI_Tickets where creationdate is not null group by creationdate) crte
full join
(select CompletionDate, count (*) as cnt from vOKPI_Tickets where CompletionDate is not null group by CompletionDate) cmpl
on crte.creationdate = cmpl.CompletionDate
You can unpivot and aggregate. A general method is:
select dte, sum(created), sum(completed)
from ((select creationdate as dte, 1 as created, 0 as completed
from vOKPI_Tickets
) union all
(select completed as dte, 0 created, 1 as completed
from vOKPI_Tickets
)
) t
group by dte;
In SQL Server, you can use cross apply for this:
select d.dt, sum(d.is_completed) count_created, sum(d.is_completed) count_completed
from vokpi_tickets t
cross apply (values (creationdate, 1, 0), (completion_date, 0, 1)) as d(dt, is_created, is_completed)
where d.dt is not null
group by d.dt

SQLite Getting multiple results with LIMIT 1

I have the following problem.
Part of a task is to determine the visitor(s) with the most money spent between 2000 and 2020.
It just looks like this.
SELECT UserEMail FROM Visitor
JOIN Ticket ON Visitor.UserEMail = Ticket.VisitorUserEMail
where Ticket.Date> date('2000-01-01') AND Ticket.Date < date ('2020-12-31')
Group by Ticket.VisitorUserEMail
order by SUM(Price) DESC;
Is it possible to output more than one person if both have spent the same amount?
Use rank():
SELECT VisitorUserEMail
FROM (SELECT VisitorUserEMail, SUM(PRICE) as sum_price,
RANK() OVER (ORDER BY SUM(Price) DESC) as seqnum
FROM Ticket t
WHERE t.Date >= date('2000-01-01') AND Ticket.Date <= date('2021-01-01')
GROUP BY t.VisitorUserEMail
) t
WHERE seqnum = 1;
Note: You don't need the JOIN, assuming that ticket buyers are actually visitors. If that assumption is not true, then use the JOIN.
Use a CTE that returns all the total prices for each email and with NOT EXISTS select the rows with the top total price:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT c.VisitorUserEMail
FROM cte c
WHERE NOT EXISTS (
SELECT 1 FROM cte
WHERE SumPrice > c.SumPrice
)
or:
WITH cte AS (
SELECT VisitorUserEMail, SUM(Price) SumPrice
FROM Ticket
WHERE Date >= '2000-01-01' AND Date <= '2020-12-31'
GROUP BY VisitorUserEMail
)
SELECT VisitorUserEMail
FROM cte
WHERE SumPrice = (SELECT MAX(SumPrice) FROM cte)
Note that you don't need the function date() because the result of date('2000-01-01') is '2000-01-01'.
Also I think that the conditions in the WHERE clause should include the =, right?

filtering with statement without using from

I want to count products showed in events between two dates. I have to fill 9 columns, each with other product type.
I would like to ask you if there are possibility to short this statement.
Below sql is first working but not effective attempt.
with events(event_id, customer_id) as (
select * from event
where start_date >= :stare_date
and end_date <= :end_date
),
select
(select count(*) from event_product where event_id in (select event_id from events where customer_id = customer.customer_id) and product_type = 'YLW') customer_ylw_products -- it works but its ugly and non effective
-------
-- repeat seven times for other type of products
-------
(select count(*) from event_product where event_id in (select event_id from events where customer_id = customer.customer_id) and product_type = 'RTL') customer_rtl_products
from customer
;
Notice that line
(select event_id from events where customer_id = customer.customer_id)
repeats about 9 times.
I've been trying to short this one by add following:
with events(event_id, customer_id) as (
select * from event
where start_date >= :stare_date
and end_date <= :end_date
),
**customer_events (event_id, customer_id) as (select * from events)**
select
(select count(*) from event_product where event_id in (select event_id from customer_events) and product_type = 'RTL') customer_rtl_products
from customers
where customer_events.customer_id = customer.customer_id -- doesnt works
having customer_events.customer_id = customer.customer_id -- doesnt works
Why don't you use case expressions?
WITH
events (event_id, customer_id)
AS (
SELECT
*
FROM event
WHERE start_date >= :stare_date
AND end_date <= :end_date
)
SELECT
*
FROM customer
LEFT JOIN (
SELECT
event_product.customer_id
, COUNT(CASE
WHEN event_product.product_type = 'YLW' THEN 1 END) AS count_YLW
, COUNT(CASE
WHEN event_product.product_type = 'RTL' THEN 1 END) AS count_RTL
FROM event_product
INNER JOIN events
ON event_product.event_id = events.event_id
GROUP BY
event_product.customer_id
) ev_counts
ON customer.customer_id = ev_counts.customer_id
;
You could do this without the CTE too if you prefer, just use what you currently have in the CTE as a derived table where events is now placed in the inner join.
footnote select * is a convenience only I don't know what fields are to be used, but they should be specified.
#Used_By_Already thanks for inspire me with inner joins between event_product and event and that Event_product doesnt have column customer_id so I simply added it!
That's my solution
with events(event_id, customer_id) as (
select * from event
where start_date >= :stare_date
and end_date <= :end_date
),
product_events (customer_id, product_type) as (
select event.customer_id, event_product.product_type
from events,event_product
where event_product.event_id = event.event_id and event_product.product_type in (''product_types'')
)
select
(select count(*) from product_events where customer_id = customer.customer_id and product_type = 'RTL') customer_rtl_products
from customers;
Performance for 50 rows in search increased from 45 seconds to only 5!
Thank you so much!