T-SQL max date and min date between two date - sql

First, thanks for your time and your help!
I have two tables:
Table 1
PersId name lastName city
---------------------------------------
1 John Smith Tirana
2 Leri Nice Tirana
3 Adam fortsan Tirana
Table 2
Id PersId salesDate
--------------------------------------------
1 1 2017-01-22 08:00:40 000
2 2 2017-01-22 09:00:00 000
3 1 2017-01-22 10:00:00 000
4 1 2017-01-22 20:00:00 000
5 3 2017-01-15 09:00:00 000
6 1 2017-01-21 09:00:00 000
7 1 2017-01-21 10:00:00 000
8 1 2017-01-21 18:55:00 000
I would like to see the first recent sales between two dates according to each city for each day I want to bring it empty if I do not have a sale
SalesDate > '2017-01-17 09:00:00 000'
and SalesDate < '2017-01-23 09:00:00 000'
Table 2, id = 5 because the record is not in the specified date range
If I wanted my results to look like
Id PersId MinSalesDate MaxSalesDate City
-----------------------------------------------------------------------------
1 1 2017-01-22 08:00:40 000 2017-01-22 20:00:00 000 Tirana
2 2 2017-01-22 09:00:00 000 null Tirana
3 3 null null Tirana
4 1 2017-01-21 09:00:00 000 2017-01-21 18:55:00 000 Tirana

You dont identify how to get ID in the result. You appear to just want Row_Number(). I will leave that out, but this should get you started. You may have to work out conversion issues in the data range check, and I havent checked the query for syntax errors, I will leave that to you.
Select T1.PersId, City
, Min(T2.salesDate) MinSalesDate
, Max(T2.salesDate) MaxSalesDate
From Table1 T1
Left Join Table2 T2
On T1.PersId = T2.PersId
And T2.salesDate Between '2017-01-17 09:00:00 000' And < '2017-01-23 09:00:00 000'
Group BY T1.PersId, T2.City

Try the following using row_number to get min and max sale dates:
SELECT
T2.Id, T1.PersId, T2.MIN_salesDate, T2.MAX_salesDate, T1.City
FROM Table1 T1
LEFT JOIN
(
SELECT MIN(Id) as Id, PersId, MIN(salesDate) as MIN_salesDate, MAX(salesDate) as MAX_salesDate
FROM
(
SELECT
*
,ROW_NUMBER() OVER (PARTITION BY PersId ORDER BY salesDate ASC) as RNKMIN
,ROW_NUMBER() OVER (PARTITION BY PersId ORDER BY salesDate DESC) as RNKMAX
FROM Table2 T2
WHERE salesDate Between '2017-01-17 09:00:00 000' And '2017-01-23 09:00:00 000'
) temp
WHERE RNKMIN = 1 or RNKMAX = 1
GROUP BY PersId
) T2
on T1.PersId = T2.PersId

Related

Include values from other rows in output Postgressql

I have a table with Infos about Objects for Rent. (Postgres v.10)
Example
id start_date end_date objekt_id person_id
1 2011-10-01 2015-10-31 5156 18268
2 2015-11-01 2018-04-30 5156 18268
3 2018-05-01 2021-03-31 5156 18269
4 2021-04-01 2021-05-15 5156
5 2021-05-16 2100-01-01 5156 18270
6 2021-03-14 2100-01-01 5160 18270
Now i want to add the last id and the next id on the same row where the objekt_id and person_id is not null.
Wished output for Object 5156
id start_date end_date objekt_id person_id last_id next_id
1 2011-10-01 2015-10-31 5156 18268 2
2 2015-11-01 2018-04-30 5156 18268 1 3
3 2018-05-01 2021-03-31 5156 18269 2 5 --here next is 5 since 4 has person_id null
4 2021-04-01 2021-05-15 5156
5 2021-05-16 2100-01-01 5156 18270 3
6 2021-03-14 2100-01-01 5160 18270
The goal in the end for me is to find out who rents something at a specific date and who rented it the last time and who is the next one to rent (if there is one).
I tried something like this but it doensn't work.
select r.id,r.start_date,r.end_date,r.objekt_id,r.person_id, rentlast.id
from rent r
inner join rent rentlast on rentlast.id = (select r3.id from rent r3 where r3.objekt_id = r.id and r3.person_id is not null and r3.id<r.id order by id desc limit 1 )
What is the best solution for this?
Better to use lag() / lead() function with PARTITION BY clause.
Something like this is the expected result?
with tbl as (
SELECT rent.*,
row_number() over (PARTITION BY objekt_id) as row_id
FROM rent
ORDER BY id)
SELECT r.id,
r.start_date,
r.end_date,
r.objekt_id,
r.person_id,
lag(row_id) over (PARTITION BY objekt_id, person_id IS NOT NULL AND objekt_id IS NOT NULL ORDER BY id) as last_row,
lead(row_id) over (PARTITION BY objekt_id, person_id IS NOT NULL AND objekt_id IS NOT NULL ORDER BY id) as next_row
FROM tbl r
order by 1;
output result

Take the last row Group By date

I need to select content statistics group By Date.
Here example of records :
id cid viewCount created_at
1 1 50 31-12-2018 18:00:00
2 1 50 01-01-2019 18:00:00
3 2 50 01-01-2019 18:00:00
4 2 100 01-01-2019 19:00:00
5 2 150 01-01-2019 20:00:00
6 3 1000 01-01-2019 15:00:00
Need to return :
id cid viewCount date
1 1 50 31-12-2018
2 1 50 01-01-2019
5 2 150 01-01-2019
6 3 1000 01-01-2019
I tried the following code
$qb = $this->createQueryBuilder('c');
$qb->select('a.id as id')
->addSelect('COALESCE(SUM(a.viewCount),0) as viewCount')
->addSelect('DATE_FORMAT(a.createdAt, \'%d-%m-%Y\') as date');
->innerJoin('c.analytics', 'a')
->groupBy('c.cid')
->addGroupBy('date')
->orderBy('a.createdAt', 'ASC');
return:
id cid viewCount date
1 1 50 31-12-2018
2 1 50 01-01-2019
3 2 50 01-01-2019
4 2 100 01-01-2019
5 2 150 01-01-2019
6 3 1000 01-01-2019
I have tried to create a subquery :
$qbLastHour = $this->createQueryBuilder('cc');
$qbLastHour->select('MAX(DATE_FORMAT(aa.createdAt, \'%H\'))')
->innerJoin('cc.analytics', 'aa')
->where('cc.id=c.id')
->groupBy('cc.cid')
->addGroupBy('s');
$qb->addSelect(sprintf("(%s) AS r", $qbLastHour->getDQl()));
But something go wrong because i dont groupBy date at the subquery.
If someone can help me. Thank you
Update
Here is an attempt, in sql again, to select only one row per date and cid based on the max time per day
SELECT id, c.cid, viewCount, max_date
FROM content a
JOIN content_analytic c ON a.id = c.content_id
RIGHT JOIN (SELECT c.cid, DATE_FORMAT(created_at, '%d-%m-%Y') dt, MAX(created_at) max_date
FROM content a
JOIN content_analytic c ON a.id = c.content_id
GROUP BY dt, c.cid) x ON x.max_date = a.created_at and x.cid = c.cid
This is how I believe the query should be in pure sql
SELECT c.cid, COALESCE(SUM(a.viewCount), 0), DATEFORMAT(a.created_at, ‘%d-%m-%Y’) as date
FROM content a
INNER JOIN content_analytic c ON a.id = c.content_id
GROUP BY c.cid, date
ORDER BY date

How duplicate a rows in SQL base on difference between date columns and divided aggregated column per duplicate row?

I have a table with some records about fuel consumption. The important columns in the table are: CONSUME_DATE_FROM and CONSUM_DATE_TO.
I want to calculate average fuel consumption per cars on a monthly basis but some rows are not in the same month. For example some have a three month difference between them and the total of gas per litre is aggregated in a single row.
Now I should find records that have difference more than a month between CONSUME_DATE_FROM and CONSUM_DATE_TO, and duplicate them in current or second table per count of month and divide the total gas per litre between related rows.
I've this table with the following data:
ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO GAS_PER_LITER
1 100 2018-10-25 2018-12-01 600
2 101 2018-07-19 2018-07-24 100
3 102 2018-12-31 2019-01-01 400
4 103 2018-03-29 2018-05-29 200
5 104 2018-02-05 2018-02-09 50
The expected output table should be as below
ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO GAS_PER_LITER
1 100 2018-10-25 2018-12-01 200
1 100 2018-10-25 2018-12-01 200
1 100 2018-10-25 2018-12-01 200
2 101 2018-07-19 2018-07-24 100
3 102 2018-12-31 2019-01-01 200
3 102 2018-12-31 2019-01-01 200
4 103 2018-03-29 2018-05-29 66.66
4 103 2018-03-29 2018-05-29 66.66
4 103 2018-03-29 2018-05-29 66.66
5 104 2018-02-05 2018-02-09 50
Or as below
ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO GAS_PER_LITER DATE_RELOAD_GAS
1 100 2018-10-25 2018-12-01 200 2018-10-01
1 100 2018-10-25 2018-12-01 200 2018-11-01
1 100 2018-10-25 2018-12-01 200 2018-12-01
2 101 2018-07-19 2018-07-24 100 2018-07-01
3 102 2018-12-31 2019-01-01 200 2018-12-01
3 102 2018-12-31 2019-01-01 200 2019-01-01
4 103 2018-03-29 2018-05-29 66.66 2018-03-01
4 103 2018-03-29 2018-05-29 66.66 2018-04-01
4 103 2018-03-29 2018-05-29 66.66 2018-05-01
5 104 2018-02-05 2018-02-09 50 2018-02-01
Can someone please help me out with this query?
I'm using oracle database
Your business rule treats the difference between CONSUME_DATE_FROM and CONSUM_DATE_TO as absolute months. So you expect the difference between 2018-10-25 and 2018-12-01 to be three months whereas the difference in days actually equates to about 1.1 months. So we can't use simple date arithmetic to get your desired output, we need to do some additional massaging of the dates.
The query below implements your desired logic by deriving the first day of the month for CONSUME_DATE_FROM and the last day of the month for CONSUME_DATE_TO, then using ceil() to round the difference up to the nearest whole number of months.
This is calculated in a subquery which is used in the main query with the old connect by level trick to multiply a record by level number of times:
with cte as (
select f.*
, ceil(months_between(last_day(CONSUM_DATE_TO)
, trunc(CONSUME_DATE_FROM,'mm'))) as diff
from fuel_consumption f
)
select cte.id
, cte.VehicleId
, cte.CONSUME_DATE_FROM
, cte.CONSUM_DATE_TO
, cte.GAS_PER_LITER/cte.diff as GAS_PER_LITER
, add_months(trunc(cte.CONSUME_DATE_FROM, 'mm'), level-1) as DATE_RELOAD_GAS
from cte
connect by level <= cte.diff
and prior cte.id = cte.id
and prior sys_guid() is not null
;
"what about if add a additional column "DATE_RELOAD_GAS" that display difference date for similar rows"
From your posted sample it seems like DATE_RELOAD_GAS is the first day of the month for each month bounded by CONSUME_DATE_FROM and CONSUM_DATE_TO. I have amended my solution to implement this rule.
By using connect by level structure with considering to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') as month I was able to resolve as below :
select ID, VehicleId, myMonth, CONSUME_DATE_FROM, CONSUM_DATE_TO,
trunc(GAS_PER_LITER/max(rn) over (partition by ID order by ID),2) as GAS_PER_LITER,
'01.'||substr(myMonth,5,2)||'.'||substr(myMonth,1,4) as DATE_RELOAD_GAS
from
(
with consumption( ID, VehicleId, CONSUME_DATE_FROM, CONSUM_DATE_TO, GAS_PER_LITER ) as
(
select 1,100,date'2018-10-25',date'2018-12-01',600 from dual union all
select 2,101,date'2018-07-19',date'2018-07-24',100 from dual union all
select 3,102,date'2018-12-31',date'2019-01-01',400 from dual union all
select 4,103,date'2018-03-29',date'2018-05-29',200 from dual union all
select 5,104,date'2018-02-05',date'2018-02-09', 50 from dual
)
select ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') myMonth,
VehicleId, c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, GAS_PER_LITER,
row_number() over (partition by ID order by ID) as rn
from dual join consumption c
on c.ID >= 2
group by ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm'), VehicleId,
c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, c.GAS_PER_LITER
connect by level <= c.CONSUM_DATE_TO - c.CONSUME_DATE_FROM + 1
union all
select ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') myMonth,
VehicleId, c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, GAS_PER_LITER,
row_number() over (partition by ID order by ID) as rn
from dual join consumption c
on c.ID = 1
group by ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm'), VehicleId,
c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, c.GAS_PER_LITER
connect by level <= c.CONSUM_DATE_TO - c.CONSUME_DATE_FROM + 1
) q
group by ID, VehicleId, myMonth, CONSUME_DATE_FROM, CONSUM_DATE_TO, GAS_PER_LITER, rn
order by ID, myMonth;
I met an interesting issue that if I consider the join condition in the subquery as c.ID >= 1 query hangs on for huge period of time, so splitted into two parts by union all
as c.ID >= 2 and c.ID = 1
Rextester Demo

CREATE TEMP TABLE BASED ON SELECT DISTINCT ON 3 COLUMNS BUT WITH 1 EXTRA COLUMN

I need to make a temporary file with in it:
Partcode, MutationDate, MovementType, Qty
Every partcode has multiple mutationdates per Movementtype (there are max 9 movementtypes possible)
I need to get the last mutationdate per movementtype per partcode and the quantity that goes with that.
An example with partcode 003307
003307 2018-05-31 1 -100
003307 2018-06-11 2 -33
003307 2018-04-25 3 +25
and so on for all 9 movementtypes.
What did I get so far:
create table #LMUT(
MutationDate T_Date
,PartCode T_Code_Part
,CumInvQty T_Quantum_Qty10_3
,MovementType T_Type_PMOverInvt
)
insert #LMUT(
MutationDate,
Partcode,
CumInvQty,
MovementType)
SELECT
cast (max(MOV.MutationDate) as date)
,MOV.PartCode
,INV.MutationQty
,INV.PMOverInvtType
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on
INV.PMMainCode=MOV.PMMainCode
WHERE
MOV.PartMovementType = 1
group by MOV.PartCode,INV.PMOverInvtType,INV.MutationQty,MOV.MutationDate
SELECT * FROM #LMUT where partcode='003007'
drop table #LMUT
results in:
2016-12-06 00:00:00.000 003007 -24.000 2
2016-09-29 00:00:00.000 003007 -24.000 2
2016-11-09 00:00:00.000 003007 -24.000 2
2016-11-22 00:00:00.000 003007 -24.000 2
2016-10-26 00:00:00.000 003007 -24.000 2
2016-09-12 00:00:00.000 003007 -42.000 2
2016-10-13 00:00:00.000 003007 -24.000 2
2016-12-03 00:00:00.000 003007 100.000 5
2017-01-12 00:00:00.000 003007 -48.000 2
2016-10-04 00:00:00.000 003007 306.000 7
Not what I need, still have 8 times type 2
What else have I tried:
SELECT distinct MOV.Partcode,INV.PMOverInvtType,mov.MutationDate
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on
INV.PMMainCode=MOV.PMMainCode
WHERE
mov.MutationDate = (SELECT MAX (c.MutationDate) FROM
dbo.T_PartMovementMain as c
inner join dbo.T_PartMovementOverInvt as d on D.PMMainCode=c.PMMainCode
WHERE
C.PartMovementType = 1 AND
C.PartCode=mov.PartCode AND
D.PMMainCode = C.PMMainCode AND
D.PMOverInvtType=inv.PMOverInvtType
)
and MOV.PartMovementType = 1 and mov.partcode='003007'
order by MOV.Partcode,INV.PMOverInvtType
Results in:
3007 2 2017-01-12 00:00:00.000
3007 5 2016-12-03 00:00:00.000
3007 7 2016-10-04 00:00:00.000
That is what I want but I need to get the Qty too.
use row_number() window function
with cte as
( SELECT MOV.*,INV.*,
row_number() over(partition by INV.PMOverInvtType order by MOV.MutationDate desc)rn
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on
INV.PMMainCode=MOV.PMMainCode
) select cte.* from cte where rn=1
Solved it like this:
create table #LMUT(
PartCode T_Code_Part
,MovementType T_Type_PMOverInvt
,MutationDate T_Date
,CumInvQty T_Quantum_Qty10_3
)
insert #LMUT(Partcode,MovementType,MutationDate,CumInvQty)
select Artikel,Type,Datum,Aant
from (
SELECT MOV.Partcode as Artikel,INV.PMOverInvtType as Type,mov.MutationDate as Datum,INV.MutationQty as Aant,
row_number() over(partition by MOV.Partcode,INV.PMOverInvtType order by MOV.Partcode,INV.PMOverInvtType,MOV.MutationDate desc) rn
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on INV.PMMainCode=MOV.PMMainCode) cse
where rn=1
select * from #LMUT order by Partcode
drop table #LMUT

PostgreSQL GROUP BY: SELECT column on MAX of another WHERE a third column = x

Let's suppose we have two tables in PostgreSQL:
Table "citizens"
country_ref citizen_name entry_date
-----------------------------------------------------
0 peter 2013-01-14 21:00:00.000
1 fernando 2013-01-14 20:00:00.000
0 robert 2013-01-14 19:00:00.000
3 albert 2013-01-14 18:00:00.000
2 esther 2013-01-14 17:00:00.000
1 juan 2013-01-14 16:00:00.000
3 egbert 2013-01-14 15:00:00.000
1 francisco 2013-01-14 14:00:00.000
3 adolph 2013-01-14 13:00:00.000
2 emilie 2013-01-14 12:00:00.000
2 jacques 2013-01-14 11:00:00.000
0 david 2013-01-14 10:00:00.000
Table "countries"
country_id country_name country_group
-------------------------------------------
0 england 0
1 spain 0
2 france 1
3 germany 1
Now I want to obtain the last entered citizen on the "citizens" table for each country of a given country_group.
My best try so far is this query (Let's call it Query_1) :
SELECT country_ref, MAX(entry_date) FROM citizens
LEFT JOIN countries ON country_id = country_ref
WHERE country_group = 1 GROUP BY country_ref
Output:
country_ref max
---------------------------------
3 2013-01-14 18:00:00
2 2013-01-14 17:00:00
So then I could do:
SELECT citizen_name FROM citizens WHERE (country_ref, entry_date) IN (Query_1)
... which will give me the output I'm looking for: albert and esther.
But I'd prefer to achieve this in a single query. I wonder if it's possible?
This should be simplest and fastest:
SELECT DISTINCT ON (i.country_ref)
i.citizen_name
FROM citizens i
JOIN countries o ON o.country_id = i.country_ref
WHERE o.country_group = 1
ORDER BY i.country_ref, i.entry_date DESC
You can easily return more columns from both tables by simply adding them to the SELECT list.
SQL Fiddle.
Details, links and explanation in this related answer:
Select first row in each GROUP BY group?
SELECT citizen_name,
country_ref,
entry_date
from (
SELECT cit.citizen_name,
cit.country_ref,
MAX(cit.entry_date) over (partition by cit.country_ref) as max_date,
cit.entry_date
FROM citizens cit
LEFT JOIN countries cou ON cou.country_id = cit.country_ref
WHERE cou.country_group = 1
) t
where max_date = entry_date
SQLFiddle demo: http://www.sqlfiddle.com/#!12/50776/1
Why don't you simply:
SELECT citizen_name FROM citizens WHERE (country_ref, entry_date) IN (
SELECT country_ref, MAX(entry_date) FROM citizens
LEFT JOIN countries ON country_id = country_ref
WHERE country_group = 1 GROUP BY country_ref
)
It might not be the best plan, but it depends on many factors, and it is simple to write.