Find the Biggest Number of Consecutive Occurrence of values in Table - sql

I have the following table
create table Launches (Id int, Name char)
insert into Launches values
(1, 'A'),
(2, 'A'),
(3, 'B'),
(4, 'B'),
(5, 'B'),
(6, 'B'),
(7, 'C'),
(8, 'B'),
(9, 'B')
The result should be
4 - B
From 3 to 6
Similar question -
Count Number of Consecutive Occurrence of values in Table

You can subtract an enumerated value for each name to get a constant for adjacent values that are the same. The rest is aggregation:
select top (1) name, count(*), min(id), max(id)
from (select l.*,
row_number() over (partition by name order by id) as seqnum
from #Launches l
) l
group by (id - seqnum), name
order by count(*) desc;
Here is a db<>fiddle.

Related

Get a count of an enum if its value has been changed in the past month based on an updated_at column

Apologies in advance if the title was unclear. I'll try to explain more here.
I have two tables, a non-temporal table and a temporal version of that table that stores the updated_at value of a row, and its historical values.
I want to count all the values of a row (in the non-temporal table) that have changed in the past 30 days, using the temporal table.
These values are enums.
Here's an example.
create temporary table headcount (id int, hiring_status text, pay float);
create temporary table headcount_history (id int, hiring_status text, pay float, updated_at date);
insert into headcount values
(1, 'interviewing', 1000.00),
(2, 'hired', 1000.00),
(3, 'scouting', 1000.00),
(4, 'interviewing', 100.12),
(5, 'scouting', 123.42);
insert into headcount_history values
(1, 'scouting', 1000.00, '2022-07-25'),
(1, 'scouting', 1005.00, '2022-07-26'),
(1, 'scouting', 1005.00, '2022-07-27'),
(1, 'interviewing', 1005.00, '2022-07-28'),
(2, 'scouting', 1000.00, '2022-03-20'),
(2, 'interviewing', 1000.00,'2022-04-20'),
(2, 'hired', 3230.00, '2022-4-23'),
(2, 'hired', 1000.00, '2022-4-25'),
(3, 'scouting', 1000.00, '2022-03-20'),
(3, 'interviewing', 1000.00,'2022-04-20'),
(3, 'scouting', 1000.00, '2022-7-25'),
(4, 'scouting', 1000.00, '2022-6-25'),
(4, 'interviewing', 100.12, '2022-7-25'),
(5, 'scouting', 1000.0, '2022-6-29'),
(5, 'scouting', 123.42, '2022-7-10');
/*
The goal is to find the number of hiring_statuses that were changed in the past 30 days
for each hiring_status.
this is expected output
scouting | interviewing | hired
2 | 2 | 0
here's a link to a db fiddle - https://dbfiddle.uk/?rdbms=postgres_13&fiddle=79ebffe4a28ef67d8552d36d47e967e7
I'm using postgres 13
select sum(coalesce(hired,0)) hired,
sum(coalesce(scouting,0)) scouting,
sum(coalesce(interviewing,0)) interviewing
from
(
select case when hiring_status ='hired' then h_cnt end "hired",
case when hiring_status ='scouting' then h_cnt end "scouting",
case when hiring_status ='interviewing' then h_cnt end "interviewing"
from
(
select b.hiring_status,coalesce(a.cnt,0) h_cnt
from
(select hiring_status,
count(1) cnt
from headcount_history
where updated_at between current_date and current_date - interval '-30 days'
group by hiring_status) A right outer join
(select hiring_status
from headcount_history
group by hiring_status)B
on a.hiring_status = b.hiring_status
)AA
) BB

Update same data from the same table depending on a column

I have a similar case of a table, Like the case of this link Update same data from the same table
but in my case it must update depending on the column "dependency". In other words, it updates the repetitions in the tables always leaving the most recent line and for table that only have one line it does not update. My data is like this:
I want it to be updated like this:
I tryed this code:
create table dbo.test( id int, CAR varchar(30), ACTIVE int, dependency int)
insert into dbo.test(id, CAR, ACTIVE, dependency)
values
(1, 'AAA-25-35', 0,1),
(2, 'LDB-25-35', 0,2),
(3, 'LDB-00-35', 0,2),
(4, 'LDB-25-35', 0,2),
(5, 'LDB-00-35', 0,2),
(6, 'LDC-10-10', 0,2),
(7, 'LDC-10-10', 0,2),
(8, 'LDB-00-35', 0,2),
(9, 'AAA-25-35', 0,1),
(10, 'AAA-25-35', 0,3),
(11, 'AAA-25-35', 0,3),
(12, 'BBB-25-35', 0,2),
(13, 'BBB-25-35', 0,3),
(14, 'BBB-25-35', 0,3)
GO
SELECT * FROM TEST
WITH CTE AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY CAR ORDER BY ID) AS t,
CAR,
ACTIVE
FROM Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t=1
AND EXISTS (SELECT 1 FROM CTE c WHERE c.CAR = CTE.CAR GROUP BY CAR HAVING COUNT(*) > 1)
go
SELECT * FROM test
Try changing the SELECT and WHERE clauses:
WITH CTE AS (
SELECT ROW_NUMBER() OVER(PARTITION BY CAR, dependency ORDER BY ID) AS t,
LEAD(id) OVER (PARTITION BY CAR, dependency ORDER BY ID) as next_id,
CAR,
ACTIVE
FROM Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t = 1 AND next_id IS NOT NULL

Select duplicate persons with duplicate memberships

SQL Fiddle with schema and my intial attempt.
CREATE TABLE person
([firstname] varchar(10), [surname] varchar(10), [dob] date, [personid] int);
INSERT INTO person
([firstname], [surname], [dob] ,[personid])
VALUES
('Alice', 'AA', '1/1/1990', 1),
('Alice', 'AA', '1/1/1990', 2),
('Bob' , 'BB', '1/1/1990', 3),
('Carol', 'CC', '1/1/1990', 4),
('Alice', 'AA', '1/1/1990', 5),
('Kate' , 'KK', '1/1/1990', 6),
('Kate' , 'KK', '1/1/1990', 7)
;
CREATE TABLE person_membership
([personid] int, [personstatus] varchar(1), [memberid] int);
INSERT INTO person_membership
([personid], [personstatus], [memberid])
VALUES
(1, 'A', 10),
(2, 'A', 20),
(3, 'A', 30),
(3, 'A', 40),
(4, 'A', 50),
(4, 'A', 60),
(5, 'T', 70),
(6, 'A', 80),
(7, 'A', 90);
CREATE TABLE membership
([membershipid] int, [memstatus] varchar(1));
INSERT INTO membership
([membershipid], [memstatus])
VALUES
(10, 'A'),
(20, 'A'),
(30, 'A'),
(40, 'A'),
(50, 'T'),
(60, 'A'),
(70, 'A'),
(80, 'A'),
(90, 'T');
There are three tables (as per the fiddle above). Person table contains duplicates, same people entered more than once, for the purpose of this exercise we assume that a combination of the first name, surname and DoB is enough to uniquely identify a person.
I am trying to build a query which will show duplicates of people (first name+surname+Dob) with two or more active entries in the Person table (person_membership.person_status=A) AND two or more active memberships (membership.mestatus=A).
Using the example from SQL Fiddle, the result of the query should be just Alice (two active person IDs, two active membership IDs).
I think I'm making progress with the following effort but it looks rather cumbersome and I need to remove Katie from the final result - she doesn't have a duplicate membership.
SELECT q.firstname, q.surname, q.dob, p1.personid, m.membershipid
FROM
(SELECT
p.firstname,p.surname,p.dob, count(*) as cnt
FROM
person p
GROUP BY
p.firstname,p.surname,p.dob
HAVING COUNT(1) > 1) as q
INNER JOIN person p1 ON q.firstname=p1.firstname AND q.surname=p1.surname AND q.dob=p1.dob
INNER JOIN person_membership pm ON p1.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
WHERE pm.personstatus = 'A' AND m.memstatus = 'A'
Since you are using SQL Server windows function will be handy for this scenario. The following will give you the expected output.
SELECT firstname,surname,dob,personid,memberid
from(
SELECT firstname,surname,dob,p.personid,memberid
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid) rnasc
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid desc) rndesc
FROM [StagingGRG].[dbo].[person] p
INNER JOIN person_membership pm ON p.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
where personstatus='A' and memstatus='A')a
where a.rnasc+rndesc>2
You have to add Group by and Having clause to return duplicate items only-
SELECT
person.firstname,person.surname,person.dob
FROM
person, person_membership, membership
WHERE
person.personid=person_membership.personid AND person_membership.memberid = membership.membershipid
AND
person_membership.personstatus = 'A' AND membership.memstatus = 'A'
GROUP BY
person.firstname,person.surname,person.dob
HAVING COUNT(1) > 1

SQL Split String to letters each one in row

I have case to get String from table , Split it to letters , each letter represent "active/inactive" "0/1" , then return description from other table for just all active letter IN ONE CELL ..
NOTE: Letters order as it is in other table ..
this is my case :
CREATE TABLE Strings_tab
(
Str_id NUMBER,
Str_text VARCHAR2 (40)
);
CREATE TABLE Reprsnt_Tab
(
rep_id NUMBER,
rep_text VARCHAR2 (40)
);
INSERT INTO Strings_tab VALUES (1, '1111111111000000000011111111110000000000');
INSERT INTO Strings_tab VALUES (2, '0000011111000001111100000111110000011111');
INSERT INTO Reprsnt_Tab VALUES (1, 'rep1');
INSERT INTO Reprsnt_Tab VALUES (2, 'rep2');
INSERT INTO Reprsnt_Tab VALUES (3, 'rep3');
INSERT INTO Reprsnt_Tab VALUES (4, 'rep4');
INSERT INTO Reprsnt_Tab VALUES (5, 'rep5');
INSERT INTO Reprsnt_Tab VALUES (6, 'rep6');
INSERT INTO Reprsnt_Tab VALUES (7, 'rep7');
INSERT INTO Reprsnt_Tab VALUES (8, 'rep8');
INSERT INTO Reprsnt_Tab VALUES (9, 'rep9');
INSERT INTO Reprsnt_Tab VALUES (10, 'rep10');
INSERT INTO Reprsnt_Tab VALUES (11, 'rep11');
INSERT INTO Reprsnt_Tab VALUES (12, 'rep12');
INSERT INTO Reprsnt_Tab VALUES (13, 'rep13');
INSERT INTO Reprsnt_Tab VALUES (14, 'rep14');
INSERT INTO Reprsnt_Tab VALUES (15, 'rep15');
INSERT INTO Reprsnt_Tab VALUES (16, 'rep16');
INSERT INTO Reprsnt_Tab VALUES (17, 'rep17');
INSERT INTO Reprsnt_Tab VALUES (18, 'rep18');
INSERT INTO Reprsnt_Tab VALUES (19, 'rep19');
INSERT INTO Reprsnt_Tab VALUES (20, 'rep20');
INSERT INTO Reprsnt_Tab VALUES (21, 'rep21');
INSERT INTO Reprsnt_Tab VALUES (22, 'rep22');
INSERT INTO Reprsnt_Tab VALUES (23, 'rep23');
INSERT INTO Reprsnt_Tab VALUES (24, 'rep24');
INSERT INTO Reprsnt_Tab VALUES (25, 'rep25');
INSERT INTO Reprsnt_Tab VALUES (26, 'rep26');
INSERT INTO Reprsnt_Tab VALUES (27, 'rep27');
INSERT INTO Reprsnt_Tab VALUES (28, 'rep28');
INSERT INTO Reprsnt_Tab VALUES (29, 'rep29');
INSERT INTO Reprsnt_Tab VALUES (30, 'rep30');
INSERT INTO Reprsnt_Tab VALUES (31, 'rep31');
INSERT INTO Reprsnt_Tab VALUES (32, 'rep32');
INSERT INTO Reprsnt_Tab VALUES (33, 'rep33');
INSERT INTO Reprsnt_Tab VALUES (34, 'rep34');
INSERT INTO Reprsnt_Tab VALUES (35, 'rep35');
INSERT INTO Reprsnt_Tab VALUES (36, 'rep36');
INSERT INTO Reprsnt_Tab VALUES (37, 'rep37');
INSERT INTO Reprsnt_Tab VALUES (38, 'rep38');
INSERT INTO Reprsnt_Tab VALUES (39, 'rep39');
INSERT INTO Reprsnt_Tab VALUES (40, 'rep40');
COMMIT;
this is my query :
SELECT STR_TEXT,
RTRIM (
XMLAGG (XMLELEMENT (E, DATA.REP_TEXT || ',' || CHR (10))).EXTRACT (
'//text()'),
',')
REPS
FROM ( SELECT LETTER,
STR_ID,
LVL,
STR_TEXT,
REP_TEXT
FROM ( SELECT DISTINCT SUBSTR (A.STR_TEXT, LEVEL, 1) LETTER,
A.STR_ID,
LEVEL LVL,
A.STR_TEXT
FROM STRINGS_TAB A
CONNECT BY LEVEL <= LENGTH (A.STR_TEXT) ---- HERE IS MY PROBLEM
) TXT,
( SELECT ROWNUM RN, REPRSNT_TAB.*
FROM REPRSNT_TAB
ORDER BY REP_ID) B
WHERE B.RN = TXT.LVL AND LETTER = 1
ORDER BY STR_ID, STR_TEXT, LVL) DATA
GROUP BY STR_TEXT
This query get correct data
If i put like 10 in place of "LENGTH (A.STR_TEXT)" to get first 10 letters of each string.
BUT .. If "LENGTH (A.STR_TEXT)" was so big , in my case 40 , the query will hang
so, please , advice me .. in this case ???
RESULT from my Query , IF I put "LEVEL <= 10" :
STR_TEXT REP
------------------------------- ------------------------------
000001111100000111110000011111 rep6,rep10,rep9,rep8,rep7
111111111100000000001111111111 rep1,rep10,rep9,rep8,rep7,rep6,rep5,rep4,rep3,rep2
My approach takes the CONNECT BY to an auxiliary table (using CONNECT BY LEVEL in a table with more than 1 row is not a good idea IMHO. Look here) :
with aux as (select level as lvl
from dual
connect by level <= (select max(length(str_text)) from strings_tab))
SELECT STR_TEXT,
RTRIM (
XMLAGG (XMLELEMENT (E, DATA.REP_TEXT || ',' || CHR (10))).EXTRACT (
'//text()'),
',')
REPS
FROM ( SELECT LETTER,
STR_ID,
LVL,
STR_TEXT,
REP_TEXT
FROM ( SELECT DISTINCT SUBSTR (A.STR_TEXT, LVL, 1) LETTER,
A.STR_ID,
LVL,
A.STR_TEXT
FROM STRINGS_TAB A join aux x on x.lvl <=LENGTH (A.STR_TEXT)
) TXT,
( SELECT ROWNUM RN, REPRSNT_TAB.*
FROM REPRSNT_TAB
ORDER BY REP_ID) B
WHERE B.RN = TXT.LVL AND LETTER = 1
ORDER BY STR_ID, STR_TEXT, LVL) DATA
GROUP BY STR_TEXT;
OUTPUT (WITHOUT (CHAR(10))
STR_TEXT REPS
0000011111000001111100000111110000011111 rep6,rep40,rep39,rep38,rep37,rep36,rep30,rep29,rep28,rep27,rep26,rep20,rep19,rep18,rep17,rep16,rep10,rep9,rep8,rep7
1111111111000000000011111111110000000000 rep1,rep30,rep29,rep28,rep27,rep26,rep25,rep24,rep23,rep22,rep21,rep10,rep9,rep8,rep7,rep6,rep5,rep4,rep3,rep2
This is My Answer :
SELECT str_text,
LISTAGG (DATA.REP_TEXT, ',' || CHR (10))
WITHIN GROUP (ORDER BY DATA.rn_rep)
reps
FROM (SELECT *
FROM ( SELECT str_text, SUBSTR (str_text, rn, 1) OneDigit, rn
FROM (SELECT str_text FROM STRINGS_TAB) txt,
( SELECT ROWNUM rn
FROM DUAL
CONNECT BY LEVEL <=
(SELECT MAX (LENGTH (str_text))
FROM STRINGS_TAB)) rep
WHERE REP.RN <= LENGTH(str_text)
ORDER BY Str_text, rn) xx,
( SELECT ROWNUM rn_rep, rep_text
FROM REPRSNT_TAB
ORDER BY rn_rep) desc_rep
WHERE desc_rep.rn_rep = xx.rn AND OneDigit = 1) data
GROUP BY str_text
select Str_id
,Str_text
,xmlquery('let $abc := for $i in 1 to string-length($str)
where substring($str, $i, 1) = "1"
return $data/rows/row[$i]/text()
return string-join( $abc, ",")'
passing Str_text as "str"
, (select xmlelement("rows",xmlagg(xmlelement( "row",rep_text) order by rep_id)) from Reprsnt_Tab) as "data"
returning content
) from Strings_tab
The table Reprsnt_Tab is aggregate to one xmlelemtnt. To access selected row you have to just do $doc/rows/row[$selected_row_nr]/text()

Retrieving consecutive rows (and the counts) with the same values

I've got a table with almost 10 million views and would to run this query on the latest million or hundred thousand or so.
Here's a SQL fiddle with example data and input/output: http://sqlfiddle.com/#!9/340a41
Is this even possible?
CREATE TABLE object (`id` int, `name` varchar(7), `value` int);
INSERT INTO object (`id`, `name`, `value`)
VALUES
(1, 'a', 1),
(2, 'b', 2),
(3, 'c', 100),
(4, 'a', 1),
(5, 'b', 2),
(6, 'c', 200),
(7, 'a', 2),
(8, 'b', 2),
(9, 'c', 300),
(10, 'a', 2),
(11, 'b', 2),
(12, 'a', 2),
(13, 'b', 2),
(14, 'c', 400)
;
-- Want:
-- name, max(id), count(id)
-- 'a', 4, 2
-- 'b', 14, 5
-- 'a', 12, 3
If you want the latest and the id is implemented sequentially, then you can do this using limit or top. In SQL Server:
select top 100000 o.*
from object o
order by id desc;
In MySQL, you would use limit:
select o.*
from object o
order by id desc
limit 100000
select name, count(id) cnt, max(id) max_id, max(value) max_v
from
(select
top 1000000 -- MS SQL Server
id,name,value
from myTable
limit 1000000 --mySQL
order by id desc)
group by name
remove line which doesn't match your server.