HAWQ. join in/out rows by in/out time - postgresql

HAWQ. How to join in/out rows by in/out time?
simple
thanks

I believe this is what you are trying to achieve. The trick is to use the window function "row_number()".
select sub1.car_id, sub1.id_in, sub1.cross_date_time_in, sub2.id_out, sub2.cross_date_time_out
from (
select car_id, id as id_in,
cross_date_time as cross_date_time_in,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'in') as sub1
join (select car_id, id as id_out,
cross_date_time as cross_date_time_out,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'out') as sub2 on sub1.car_id = sub2.car_id and sub1.row_num = sub2.row_num;
You can write this with common table expressions too if you prefer that format.
with sub1 as (select car_id, id as id_in, cross_date_time as cross_date_time_in,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'in'),
sub2 as (select car_id, id as id_out, cross_date_time as cross_date_time_out,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'out')
select sub1.car_id, sub1.id_in, sub1.cross_date_time_in,
sub2.id_out, sub2.cross_date_time_out
from sub1
join sub2 on sub1.car_id = sub2.car_id and sub1.row_num = sub2.row_num;

create table source_table
(
id INT
,car_id INT
,direction text
,cross_date_time TIMESTAMP
);
insert into source_table
values (1, 1,'in', to_timestamp('2017-02-02-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 1,'in', to_timestamp('2017-02-12-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 1,'in', to_timestamp('2017-02-18-10:20:15', 'yyyy-MM-dd hh:mi:ss'));;
insert into source_table
values (1, 1,'in', to_timestamp('2017-02-25-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 1,'out', to_timestamp('2017-02-08-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 1,'out', to_timestamp('2017-02-09-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 1,'out', to_timestamp('2017-02-27-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'in', to_timestamp('2017-02-02-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'in', to_timestamp('2017-02-12-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'in', to_timestamp('2017-02-18-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'out', to_timestamp('2017-02-08-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'out', to_timestamp('2017-02-14-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'out', to_timestamp('2017-02-27-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 2,'out', to_timestamp('2017-02-29-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 3,'in', to_timestamp('2017-02-02-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 3,'in', to_timestamp('2017-02-12-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 3,'out', to_timestamp('2017-02-08-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
insert into source_table
values (1, 3,'out', to_timestamp('2017-02-14-10:20:15', 'yyyy-MM-dd hh:mi:ss'));
select sub1.car_id, sub1.id_in, sub1.cross_date_time_in, sub2.id_out, sub2.cross_date_time_out
from (
select car_id, id as id_in,
cross_date_time as cross_date_time_in,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'in') as sub1
join (select car_id, id as id_out,
cross_date_time as cross_date_time_out,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'out') as sub2 on sub1.car_id = sub2.car_id and sub1.row_num = sub2.row_num;
Wrong result.
1 1 2017-02-12 10:20:15.000000 1 2017-02-09 10:20:15.000000
if use a left join variant:
select sub1.car_id, sub1.id_in, sub1.cross_date_time_in, sub2.id_out, sub2.cross_date_time_out
from (
select car_id, id as id_in,
cross_date_time as cross_date_time_in,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'in') as sub1
left join (select car_id, id as id_out,
cross_date_time as cross_date_time_out,
row_number() over (partition by car_id order by cross_date_time) as row_num
from source_table
where direction = 'out') as sub2 on sub1.car_id = sub2.car_id and sub1.row_num = sub2.row_num;
wrong results:
1 1 2017-02-12 10:20:15.000000 1 2017-02-09 10:20:15.000000
1 1 2017-02-18 10:20:15.000000 1 2017-02-27 10:20:15.000000
1 1 2017-02-25 10:20:15.000000

Related

recursive query to replicate/imitate dense_rank

BEGIN;
CREATE temp TABLE teacher (
name text,
salary numeric
);
INSERT INTO teacher
VALUES ('b1', 90000);
INSERT INTO teacher
VALUES ('f1', 87000);
INSERT INTO teacher
VALUES ('a', 65000),
('b', 90000),
('c', 40000),
('d', 95000),
('e', 60000),
('f', 87000);
COMMIT;
query
with recursive cte as(
(select name, salary, 1 as rn
from teacher order by salary desc limit 1)
union all
select l.* from cte c cross join lateral(
select name, salary, rn + 1 from teacher t
where t.salary < c.salary
order by salary desc
limit 1
) l
)
table cte order by salary desc;
If all salary are distinct,then above mentioned query can imitate as rank/row_number.
I am wondering how to use recursive query to replicate/imitate dense_rank.
related post: https://dba.stackexchange.com/questions/286627/get-top-two-rows-per-group-efficiently

Generate multiple rows for single column

I've data as below:
Create table #student(id int, name varchar(20))
create table #test(id int, test_Date datetime, test_type varchar(20))
Insert int #student values (1, 'A')
insert into #student values (2, 'B')
insert into #student values (3, 'C')
insert into #test values (1, '1/1/2022', 'Math')
insert into #test values (1, '1/2/2022', 'Eng')
insert into #test values (1, '1/3/2022', 'Science')
insert into #test values (2, '2/1/2022', 'Math')
insert into #test values (2, '2/2/2022', 'Eng')
insert into #test values (3, '3/1/2022', 'Math')
insert into #test values (3, '3/2/2022', 'Science')
Need data in the below format:
Output
Looks like you simply just want to join your #student table to your #test table.
SELECT s.id, s.name, t.test_date. t.test_type
FROM #student s
JOIN #test t
ON s.id = t.id
ORDER BY s.id, t.test_date, t.test_type
This will show the id, name, test date and test type per student.
Ordered by the student id, test date and test type.
try this
;with t0 AS (
SELECT s.id, s.name, t.test_date, t.test_type
,dense_rank() over(partition by s.id order by s.id ,test_date) AS drid
,dense_rank() over(partition by s.id,name order by s.id ,test_date) AS drname
FROM #student s
JOIN #test t
ON s.id = t.id
)
select case drid when 1 then id else null end as id
,case drname when 1 then name else null end as
name,test_Date,test_type from t0
ORDER BY t0.id, t0.name

Is there a better way to do update PostgreSQL

I'm trying to update the ended_at and active columns in the test_subscription table when the max period_end has not passed.
I'm using the below query but I doubt it's the most idiomatic way. Any suggestions on improvements are very much welcome.
Creating the tables:
CREATE TABLE test_subscription (
id INTEGER PRIMARY key,
started_at timestamp,
ended_at TIMESTAMP,
active boolean
);
CREATE TABLE test_invoice (
id INTEGER PRIMARY key,
subscription_id INTEGER,
period_start timestamp,
period_end timestamp
);
INSERT INTO test_subscription (id, started_at, ended_at, active)
values(1, '2017-01-01 00:00:00', NULL, TRUE);
INSERT INTO test_subscription (id, started_at, ended_at, active)
values(2, '2017-01-01 00:00:00', NULL, TRUE);
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(1, 1, '2017-01-01 00:00:00', '2017-12-01 00:00:00');
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(2, 1, '2017-12-02 00:00:00', '2019-12-01 00:00:00');
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(3, 2, '2017-01-01 00:00:00', '2017-12-01 00:00:00');
I'm updating using the below.
UPDATE test_subscription
SET ended_at = (CASE WHEN (SELECT
MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
) < now()
THEN (SELECT MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
)
ELSE NULL
end),
active = (CASE WHEN (SELECT MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
) < now()
THEN TRUE
ELSE FALSE
end);
Updates like that are usually faster if you first collect all the aggregates, then run the update using that intermediate result. Co-related sub-queries tend to be much slower.
update test_subscription s
set ended_at = case when t.latest_end < current_timestamp then t.latest_end end,
active = t.latest_end < current_timestamp
from (
select s.id,
max(i.period_end) as latest_end
from test_subscription s
join test_invoice i on s.id = i.subscription_id
group by s.id
) t
where t.id = s.id;
Online example: http://rextester.com/NMMF41667
You can put the MAX within CASE
UPDATE test_subscription s
SET ( ended_at, active ) = (SELECT MAX(CASE
WHEN period_end < NOW() THEN
period_end
END),
MAX(CASE
WHEN period_end < NOW() THEN 'TRUE'
ELSE 'FALSE'
END) :: BOOLEAN
FROM test_invoice i
WHERE s.id = i.subscription_id);
Demo

Multi-INSERT with unchangeable param

Is there any way to INSERT multiple values with one from DB that unchangable?
I thought about WITH but without success:
WITH t as (SELECT date_trunc('hour', NOW()))
INSERT INTO my_table(ID, TIME) VALUES (1,t),(2,t);
No need for the CTE, just use a plain SELECT as the source for the insert:
insert into my_table (id, time)
select i, date_trunc('hour', NOW())
from generate_series(1,2) i;
If you really want the CTE, you need to select from it in the values clause:
WITH t as (
SELECT date_trunc('hour', NOW()) hour_t
)
INSERT INTO my_table(ID, TIME)
VALUES
(1, (select hour_t from t)),
(2, (select hour_t from t));

DATEDIFF On Dates

I've been struggling with this but I'm not really good at tsql.
This is what I got, and I can't have the DateTime calculates all right. I'm getting the sum between A and B but not the total sum. For example in the last column I have a 0 which is getting me back to -x.
Here is the procedure, and some of the data are like this:
Code_Procedure date_evenement codes_situation
---------------------------------------------------------------
000079500000 2013-05-21 13:07:00.000 COMCFM
000079500000 2013-05-21 20:24:00.000 PCHCFM
000079500000 2013-05-22 09:58:00.000 PCHCFM
000079500000 2013-05-23 00:00:00.000 AARCFM
000079500000 2013-05-23 00:00:00.000 LIVCFM
000079600000 2013-05-21 13:07:00.000 COMCFM
000079600000 2013-05-21 20:24:00.000 PCHCFM
000079600000 2013-05-22 11:18:00.000 PCHCFM
000079600000 2013-05-23 00:00:00.000 AARCFM
000079600000 2013-05-23 00:00:00.000 LIVCFM
Here is the proc:
DECLARE #COMCFM TABLE(numero_colis VARCHAR(25), date_evenement DATETIME);
INSERT #COMCFM SELECT TOP(5) numero_colis, date_evenement FROM cartitem_colis_postaux_etats WHERE (code_situation = 'PCH' AND code_justification = 'CFM')
WHILE (SELECT COUNT(*) FROM #COMCFM) > 0
BEGIN
DECLARE #Colis TABLE(numero_colis VARCHAR(25), date_evenement DATETIME, code_situation_code_justification NVARCHAR(32));
INSERT #Colis SELECT numero_colis, date_evenement, code_situation + code_justification FROM cartitem_colis_postaux_etats WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM) ORDER BY numero_colis, date_evenement
;WITH CTE AS
(
Select DISTINCT
*
,ROW_NUMBER() OVER(PARTITION BY numero_colis ORDER BY date_evenement ASC) Rn FROM #Colis
),CTE1 AS
(
SELECT DISTINCT
A.*
,DATEDIFF(mi, B.date_evenement, A.date_evenement) AS DIFF
FROM CTE A INNER JOIN CTE B On B.Rn + 1 = A.Rn
UNION All
SELECT A.*, 0 FROM CTE A Where Rn = 1
)
SELECT
A.*
,ISNULL((
SELECT
A.DIFF + B.DIFF
FROM CTE1 AS B
WHERE A.numero_colis = B.numero_colis
AND A.Rn = B.Rn + 1), 0) AS Sums
FROM CTE1 AS a
ORDER BY numero_colis, Rn ASC
DELETE FROM #Colis
DELETE FROM #COMCFM WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM)
END
I'm not really sure what you would like to achieve. Do you need date differencies as a cummulated value? If you need this, change your BEGIN-END block of your stored procedure with this code
BEGIN
DECLARE #Colis TABLE(numero_colis VARCHAR(25), date_evenement DATETIME, code_situation_code_justification NVARCHAR(32));
INSERT #Colis SELECT numero_colis, date_evenement, code_situation + code_justification FROM cartitem_colis_postaux_etats WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM) ORDER BY numero_colis, date_evenement
;WITH CTE AS
(
SELECT DISTINCT
*,
ROW_NUMBER() OVER(PARTITION BY numero_colis ORDER BY date_evenement ASC) Rn
FROM #Colis
),CTE1 AS
(
SELECT A.*, 0 AS CummulatedDiff
FROM CTE A
WHERE Rn = 1
UNION ALL
SELECT DISTINCT A.*, B.CummulatedDiff + DATEDIFF(mi, B.date_evenement, A.date_evenement) AS CummulatedDiff
FROM CTE AS A INNER JOIN
CTE1 AS B ON B.Rn + 1 = A.Rn AND B.numero_colis = A.numero_colis
)
SELECT *
FROM CTE1 AS a
ORDER BY numero_colis, Rn ASC
DELETE FROM #Colis
DELETE FROM #COMCFM WHERE numero_colis = (SELECT TOP(1) numero_colis FROM #COMCFM)
END
I hope this takes you further to your goal.