Transpose and join one column SQL - sql

I have a table which will be filled in the following manner:
ID
MODELID
PROPERTYID
V
Q
T
TYPE
1
LotNumber
NULL
1243582
NULL
2021-10-08 00:00:00.000
NULL
2
GoodStrips
NULL
39288
NULL
2021-10-08 00:00:00.000
NULL
3
StripProc
NULL
492
NULL
2021-10-08 00:00:00.000
NULL
4
StripRaw
NULL
883
NULL
2021-10-08 00:00:00.000
NULL
5
LabelProc
NULL
414
NULL
2021-10-08 00:00:00.000
NULL
6
LabelRaw
NULL
54
NULL
2021-10-08 00:00:00.000
NULL
7
SmallTips
NULL
101
NULL
2021-10-08 00:00:00.000
NULL
8
LongTips
NULL
65
NULL
2021-10-08 00:00:00.000
NULL
For each block of 8 rows, the timestamp will be identical.
Ideally, I'd like to make another table or view from this initial table where my lot number or timestamp would act as an ID column, and all the other values would be placed in the same row, like so:
LotNumber
GoodStrips
StripProc
StripRaw
LabelProc
LabelRaw
SmallTips
LongTips
T
1243582
39288
492
883
414
54
101
65
2021-10-08 00:00:00.000
I've been trying to get an inner join working to no avail.
My attempt at doing the first few as a test:
Select m1.T, m1.MODELID, m2.V, m3.V
from Rejects945 m1
inner join Rejects945 m2 on m2.T = m1.T
inner join Rejects945 m3 on m3.T = m1.T
where m2.V = 'GoodStrips'
where m3.V = 'StripProc'
where MODELID = 'LotNumber'
I get the following error:
Msg 156, Level 15, State 1, Line 6
Incorrect syntax near the keyword 'where'
Any help is greatly appreciated.

You can do this with the PIVOT function.
PIVOT & UNPIVOT
See sample code below:
CREATE TABLE #Test
(
ID INT
,MODELID VARCHAR(100)
,V INT
,T DATETIME
)
INSERT #Test (ID, MODELID, V, T)
VALUES (1,'LotNumber',1243582,'8/10/2021 12:00:00 AM')
,(2,'GoodStrips',39288,'8/10/2021 12:00:00 AM')
,(3,'StripProc',492,'8/10/2021 12:00:00 AM')
,(4,'StripRaw',883,'8/10/2021 12:00:00 AM')
,(5,'LabelProc',414,'8/10/2021 12:00:00 AM')
,(6,'LabelRaw',54,'8/10/2021 12:00:00 AM')
,(7,'SmallTips',101,'8/10/2021 12:00:00 AM')
,(8,'LongTips',65,'8/10/2021 12:00:00 AM')
,(9,'LotNumber',2345234,'9/10/2021 12:00:00 AM')
,(10,'GoodStrips',4543,'9/10/2021 12:00:00 AM')
,(11,'StripProc',455,'9/10/2021 12:00:00 AM')
,(12,'StripRaw',43,'9/10/2021 12:00:00 AM')
,(13,'LabelProc',24,'9/10/2021 12:00:00 AM')
,(14,'LabelRaw',5,'9/10/2021 12:00:00 AM')
,(15,'SmallTips',2,'9/10/2021 12:00:00 AM')
,(16,'LongTips',666,'9/10/2021 12:00:00 AM')
select LotNumber
,GoodStrips
,StripProc
,StripRaw
,LabelProc
,LabelRaw
,SmallTips
,LongTips
,t
from
(
select v, MODELID, t
from #Test
) d
pivot
(
max(v)
for MODELID in (LotNumber
,GoodStrips
,StripProc
,StripRaw
,LabelProc
,LabelRaw
,SmallTips
,LongTips
)
) piv;

Related

PIVOT datetime and ORDER column values of multiple rows

I have a table with values which are not in order
Id
DateTime
Status
1
2022-03-01 18:00:00.000
Stop1
2
2022-03-01 08:00:00.000
Start
3
2022-03-01 20:00:00.000
Stop2
4
2022-03-02 09:00:00.000
Start
5
2022-03-01 10:00:00.000
Stop2
6
2022-03-02 11:00:00.000
Finish
7
2022-03-01 14:00:00.000
Start
8
2022-03-02 10:00:00.000
Stop1
where Status can be 'Start', 'Stop1', 'Stop2', or 'Finish'.
I need the timeline like this, where the values are pivoted in the order (from the earliest to the latest; id is not relevant at this point)
Id
Start
Stop1
Stop2
Finish
2
2022-03-01 08:00:00
NULL
2022-03-01 10:00:00
NULL
7
2022-03-01 14:00:00
2022-03-01 18:00:00
2022-03-01 20:00:00
NULL
4
2022-03-02 09:00:00
2022-03-02 10:00:00
NULL
2022-03-02 11:00:00
After I PIVOTed it in SQL Server
SELECT *
FROM (
SELECT Id, DateTime, Status FROM table
) t
PIVOT (
MAX(DateTime)
FOR Status IN (Start, Stop1, Stop2, Finish)
) p
I got
Id
Start
Stop1
Stop2
Finish
2
2022-03-01 08:00:00
NULL
NULL
NULL
5
NULL
NULL
2022-03-01 10:00:00
NULL
7
2022-03-01 14:00:00
NULL
NULL
NULL
1
NULL
2022-03-01 18:00:00
NULL
NULL
3
NULL
NULL
2022-03-01 20:00:00
NULL
6
NULL
NULL
NULL
2022-03-02 11:00:00
8
NULL
2022-03-02 10:00:00
NULL
NULL
4
2022-03-02 09:00:00
NULL
NULL
NULL
How can I get that timeline?
Perhaps this will help. The window functions can be invaluable
Also, remember to "FEED" your pivot with only the required columns.
Example
Select *
From (
Select id = min(case when Status='Start' then ID end) over (partition by Grp)
,DateTime
,Status
From (
Select *
,Grp = sum( case when [Status]='Start' then 1 else 0 end) over (order by datetime)
from YourTable
) A
) src
Pivot ( max(DateTime) FOR Status IN (Start, Stop1, Stop2, Finish) ) p
Results

Passing data from one table to a block code - Oracle

I have a table dates_2019 with all the weekdays dates for 2019 as below:-
TS_RANGE_BEGIN |TS_RANGE_END
2019-01-01 17:00:00 |2019-01-02 17:00:00
2019-01-02 17:00:00 |2019-01-03 17:00:00
2019-01-03 17:00:00 |2019-01-04 17:00:00
2019-01-04 17:00:00 |2019-01-07 17:00:00
2019-01-07 17:00:00 |2019-01-08 17:00:00
2019-01-08 17:00:00 |2019-01-09 17:00:00
My insert query as below:-
insert into report_2019(ab,app_name,status,sub_count,category,last_modified_timestamp)
with T as (
select id,ab,app_name,status,trunc(last_modified_timestamp),
row_number() over(partition by id, ab order by p_message_id desc, message_id desc) lastest_status_order_id
,p_message_id
,LAST_MODIFIED_TIMESTAMP,reporting_purpose
from (
select
id,
ab
, app_name,
status
,p.message_id p_message_id
,s.message_id
,s.LAST_MODIFIED_TIMESTAMP, reporting_purpose
from table_a s, table_b d, table_c k, table_d t,
table_e p
where s.LAST_MODIFIED_TIMESTAMP > to_timestamp('2019-01-01 17', 'YYYY-MM-DD HH24')
and s.LAST_MODIFIED_TIMESTAMP <= to_timestamp('2019-01-02 17', 'YYYY-MM-DD HH24')
and .....
) a
)
select * from (
select ab, app_name, status, count(*) subtotal,
'Reporting' as v_category,trunc(last_modified_timestamp)
from T where lastest_status_order_id = 1
and LAST_MODIFIED_TIMESTAMP <= to_timestamp('2019-01-02 17', 'YYYY-MM-DD HH24')
group by ab,app_name,status,trunc(last_modified_timestamp))a order by ab, app_name;
The original idea was to join both the tables dates_2019 and T and get the results for the whole year as below:-
where s.LAST_MODIFIED_TIMESTAMP > c.ts_range_begin
and s.LAST_MODIFIED_TIMESTAMP <= c.ts_range_end
....
However the temp tablespace is low and i got the below errors:-
[Error Code: 12801, SQL State: 72000] ORA-12801: error signaled in
parallel query server P004 ORA-01555: snapshot too old: rollback
segment number 30 with name "_SYSSMU30_326584413$" too small
The option now is to insert the data one day at a time in a block.
Could you please help me with the solution?
Thanks,

Generate a table with a range of timestamps - Oracle SQL

I am trying to create a table with 2 columns in the below format with all the dates of 2019:-
START_TIME END_TIME
2010-01-01 17:00:00|2019-01-02 17:00:00
2019-01-02 17:00:00|2019-01-03 17:00:00
2019-01-03 17:00:00|2019-01-04 17:00:00
...
...
2019-12-31 17:00:00|2020-01-01 17:00:00
Could you please help troubleshoot the error in this?
Please suggest any optimized way of achieving this.
CREATE TABLE s.dates_2019
(
ts_range_begin timestamp(6),
ts_range_end timestamp(6),
);
insert into s.dates_2019 (ts_range_begin)
select
to_timestamp('12/31/2018 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') + n.n
from
(select rownum n
from ( select 1 just_a_column
from dual
connect by level <=
to_timestamp('12/31/2019 05:00 PM', 'YYYY-MM-DD HH24:MI:SS')
- to_timestamp('12/31/2018 05:00 PM', 'YYYY-MM-DD HH24:MI:SS')
+ 1
) t
) n
where
to_timestamp('12/31/2018 05:00 PM','YYYY-MM-DD HH24:MI:SS') + n.n <= to_timestamp('12/31/2019 05:00 PM','YYYY-MM-DD HH24:MI:SS')
insert into s.dates_2019 (ts_range_end)
select
to_timestamp('2019-01-01 05:00 PM', 'YYYY-MM-DD HH24:MI:SS') + n.n
from
(select rownum n
from ( select 1 just_a_column
from dual
connect by level <=
to_timestamp('2020-01-01 05:00 PM', 'YYYY-MM-DD HH24:MI:SS')
- to_timestamp('2019-01-01 05:00 PM', 'YYYY-MM-DD HH24:MI:SS')
+ 1
) t
) n
where
to_timestamp('2019-01-01 05:00 PM','YYYY-MM-DD HH24:MI:SS') + n.n <= to_timestamp('2020-01-01 05:00 PM','YYYY-MM-DD HH24:MI:SS')
Error is :-
[Error Code: 30081, SQL State: 99999] ORA-30081: invalid data type for datetime/interval arithmetic
How about this?
SQL> alter session set nls_date_format = 'yyyy-mm-dd hh24:mi';
Session altered.
SQL> with dates as
2 (select date '2019-01-01' + 17/24 + level - 1 datum
3 from dual
4 connect by level <= date '2020-01-01' - date '2019-01-01' + 1
5 ),
6 staend as
7 (select datum as start_time,
8 lead(datum) over (order by datum) as end_time
9 from dates
10 )
11 select start_time,
12 end_time
13 from staend
14 where end_time is not null
15 order by start_time;
START_TIME END_TIME
---------------- ----------------
2019-01-01 17:00 2019-01-02 17:00
2019-01-02 17:00 2019-01-03 17:00
2019-01-03 17:00 2019-01-04 17:00
2019-01-04 17:00 2019-01-05 17:00
<snip>
2019-12-30 17:00 2019-12-31 17:00
2019-12-31 17:00 2020-01-01 17:00
365 rows selected.
SQL>
If you want to insert dates into a table, you don't really need a timestamp - date will do.
SQL> create table dates_2019
2 (ts_range_begin date,
3 ts_range_end date
4 );
Table created.
SQL> insert into dates_2019 (ts_range_begin, ts_range_end)
2 with dates as
3 (select date '2019-01-01' + 17/24 + level - 1 datum
4 from dual
5 connect by level <= date '2020-01-01' - date '2019-01-01' + 1
6 ),
7 staend as
8 (select datum as start_time,
9 lead(datum) over (order by datum) as end_time
10 from dates
11 )
12 select start_time,
13 end_time
14 from staend
15 where end_time is not null
16 order by start_time;
365 rows created.
SQL>
If you want to aggregate weekends, consider using offset in the lead analytic function. That offset depends on day name (Friday). Also, remove weekend days from the result set (line #21, where day not in ('sat', 'sun')).
SQL> insert into dates_2019 (ts_range_begin, ts_range_end)
2 with dates as
3 (select date '2019-01-01' + 17/24 + level - 1 datum,
4 --
5 to_char(date '2019-01-01' + 17/24 + level - 1,
6 'fmdy', 'nls_date_language = english') day
7 from dual
8 connect by level <= date '2020-01-01' - date '2019-01-01' + 1
9 ),
10 staend as
11 (select datum as start_time,
12 day,
13 lead(datum, case when day = 'fri' then 3
14 else 1
15 end) over (order by datum) as end_time
16 from dates
17 )
18 select start_time,
19 end_time
20 from staend
21 where day not in ('sat', 'sun')
22 and end_time is not null;
261 rows created.
SQL> select * from dates_2019 order by ts_range_begin;
TS_RANGE_BEGIN TS_RANGE_END
---------------- ----------------
2019-01-01 17:00 2019-01-02 17:00
2019-01-02 17:00 2019-01-03 17:00
2019-01-03 17:00 2019-01-04 17:00
2019-01-04 17:00 2019-01-07 17:00 --> aggregated
2019-01-07 17:00 2019-01-08 17:00
2019-01-08 17:00 2019-01-09 17:00
2019-01-09 17:00 2019-01-10 17:00
2019-01-10 17:00 2019-01-11 17:00
2019-01-11 17:00 2019-01-14 17:00 --> aggregated
2019-01-14 17:00 2019-01-15 17:00
2019-01-15 17:00 2019-01-16 17:00
<snip>
I think your actual error is because subtracting timestamps returns an interval, whereas you're using the result as a number in CONNECT BY LEVEL. You could cast the timestamps as dates (you might find the answers here useful) or use an interval expression to get the day component between the timestamps.
But if this is your actual SQL and not a simplification, I suggest just using dates in the CONNECT BY (you can still keep timestamps in your table if that's what you want) and doing something like...
CREATE TABLE dates_2019
(
ts_range_begin timestamp(6),
ts_range_end timestamp(6)
);
insert into dates_2019 (ts_range_begin)
select
to_timestamp('2018-12-31 17', 'YYYY-MM-DD HH24') + rownum
from
dual
connect by level <= to_date('2019-12-31 17', 'YYYY-MM-DD HH24') - to_date('2018-12-31 17', 'YYYY-MM-DD HH24')
;
update dates_2019 SET ts_range_end = ts_range_begin + 1;
... which I tested in Oracle 18c, but probably works 10g.

How To find attendance Mispunch in sql

I have a table tbl_attendance in SQL Server with data is in this format
card_no adate time
-----------------------------------------
13 2016-08-01 2016-08-01 09:30:00
13 2016-08-01 2016-08-01 11:00:00
13 2016-08-01 2016-08-01 12:00:00
13 2016-08-01 2016-08-01 15:00:00
13 2016-08-01 2016-08-01 16:00:00
13 2016-08-02 2016-08-02 09:30:00
but when I execute my query, I want to get results in this format
card_no adate time_in time_in
----------------------------------------------------------------
13 2016-08-01 2016-08-01 09:30:00 2016-08-01 11:00:00
13 2016-08-01 2016-08-01 12:00:00 2016-08-01 15:00:00
13 2016-08-01 2016-08-01 16:00:00 null
13 2016-08-02 2016-08-02 09:30:00 null
Please help as soon as possible
Try this trick
;WITH cte
AS (SELECT *,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)%2) + 1 AS seq_no,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)/2) + 1 rang_no
FROM Yourtable)
SELECT [card_no],
[adate],
time_in = Min(CASE WHEN seq_no = 1 THEN [time] END),
time_in = Min(CASE WHEN seq_no = 2 THEN [time] END)
FROM cte
GROUP BY [card_no],
[adate],
rang_no
Demo
Schema Setup
IF Object_id('tempdb.dbo.#Table1') is not null
DROP TABLE #Table1
CREATE TABLE #Table1
([card_no] int, [adate] date, [time] datetime);
Sample Data
INSERT INTO #Table1
([card_no], [adate], [time])
VALUES
(13, '2016-08-01 00:00:00', '2016-08-01 09:30:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 11:00:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 12:00:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 15:00:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 16:00:00'),
(13, '2016-08-02 00:00:00', '2016-08-02 09:30:00');
Query
;WITH cte
AS (SELECT *,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)%2) + 1 AS seq_no,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)/2) + 1 rang_no
FROM #Table1)
SELECT [card_no],
[adate],
time_in = Min(CASE WHEN seq_no = 1 THEN [time] END),
time_in = Min(CASE WHEN seq_no = 2 THEN [time] END)
FROM cte
GROUP BY [card_no],
[adate],
rang_no
Result:
card_no adate time_in time_in
------ ---------- ----------------------- -----------------------
13 2016-08-01 2016-08-01 09:30:00.000 2016-08-01 11:00:00.000
13 2016-08-01 2016-08-01 12:00:00.000 2016-08-01 15:00:00.000
13 2016-08-01 2016-08-01 16:00:00.000 NULL
13 2016-08-02 2016-08-02 09:30:00.000 NULL
Another way with OUTER APPLY (is used to get next row with time > than in current row) and ROW_NUMBER (with %2 to get only odd rows):
SELECT t.card_no,
t.adate,
t.[time],
t.time_in
FROM (
SELECT y.*,
p.[time] as time_in,
ROW_NUMBER() OVER (PARTITION BY y.card_no, y.adate ORDER BY y.[time])%2 as seq
FROM YourTable y
OUTER APPLY (
SELECT TOP 1 *
FROM YourTable
WHERE [time] > y.[time] and [adate] = y.adate
ORDER BY [time] ASC
) p
) as t
WHERE t.seq = 1
ORDER BY [time]
Output:
card_no adate time time_in
13 2016-08-01 2016-08-01 09:30:00 2016-08-01 11:00:00
13 2016-08-01 2016-08-01 12:00:00 2016-08-01 15:00:00
13 2016-08-01 2016-08-01 16:00:00 NULL
13 2016-08-02 2016-08-02 09:30:00 NULL

SQL query stuck - comparison on different lines

I m working on a very weird problem with SQL where I have to compare previous rows
Number start_date end_date
----- ------- ------------
1 2011-06-07 00:00:00.000 2011-07-10 00:00:00.000
2 2011-10-11 00:00:00.000 2011-10-11 00:00:00.000
3 2011-10-26 00:00:00.000 2011-10-29 00:00:00.000
4 2011-10-29 00:00:00.000 2011-11-15 00:00:00.000
Here , I have to compare the start_date and end_date on the two different line and create a view out of it.
(If the start_date is less than the previous end_date , then criteria is set to 1).
Well it should compare 2011-10-26 00:00:00.000 for 3 and 2011-10-27 00:00:00.000 on 2 for 30 days
Number start_date end_date Criteria
----- ----------- ---------------- ------------
1 2011-06-07 00:00:00.000 2011-07-10 00:00:00.000 0
2 2011-10-11 00:00:00.000 2011-10-11 00:00:00.000 0
3 2011-10-26 00:00:00.000 2011-10-29 00:00:00.000 1
4 2011-10-30 00:00:00.000 2011-11-15 00:00:00.000 1
I m confused how should I proceed with this.
Any help would be helpful !!!!
Thanks !!!
The most straightforward way to do this is to use a subquery:
select A.number, a.start_date, a.end_date,
CASE WHEN start_date < dateadd(d,30,(select TOP(1) b.end_date
from mytable B
where B.number < A.number
order by B.number desc)) then 1 else 0 end Criteria
from mytable A
Note: If the start date is the 29th day following the previous row's end date, Criteria becomes 1. By the 30th day onwards, it is 0. Tweak the 30 in the query as required.
Sample:
create table mytable (
Number int primary key,
start_date datetime,
end_date datetime);
insert mytable
select 1, '2011-06-07', '2011-07-10' union all
select 2, '2011-10-11', '2011-10-27' union all
select 3, '2011-10-26', '2011-10-29' union all
select 4, '2011-10-29', '2011-11-15'
Result:
number start_date end_date Criteria
1 2011-06-07 00:00:00.000 2011-07-10 00:00:00.000 0
2 2011-10-11 00:00:00.000 2011-10-27 00:00:00.000 0
3 2011-10-26 00:00:00.000 2011-10-29 00:00:00.000 1
4 2011-10-29 00:00:00.000 2011-11-15 00:00:00.000 0
Try using case like this:
create view vDates as
select Number,start_date,end_date,
case
when start_date<end_date
then 0
else 1
end as Criteria
from tab
SQL Fiddle Demo
A more readable way is create a function and send the correct dates:
Function:
create function [dbo].[CompareDates] (
#START_DATE datetime,
#PREVIOUS_END_DATE datetime
)
RETURNS int
AS
BEGIN
if #START_DATE < #PREVIOUS_END_DATE
return 1
return 0
END
Query (using subquery):
declare #dates table
(
number int,
start datetime,
end_date datetime
)
insert into #dates values
(1, '2011-06-07 00:00:00.000', '2011-07-10 00:00:00.000'),
(2, '2011-10-11 00:00:00.000', '2011-10-27 00:00:00.000'),
(3, '2011-10-26 00:00:00.000', '2011-10-29 00:00:00.000'),
(4, '2011-10-29 00:00:00.000', '2011-11-15 00:00:00.000')
select *, dbo.CompareDates(dates.end_date, dates.previous_end_date) from
(
select number, start, end_date,
(select TOP 1 end_date
from #dates d2
where d2.number < d1.number
order by d2.number desc) as previous_end_date
from #dates d1
) dates