Postgresql: only keep unique values from integer array - postgresql

Let's say I have an array of integers
1 6 6 3 3 8 4 4
It will be always of the form n*(pairs of number) + 2 (unique numbers).
Is there an efficient way of keeping only the 2 uniques values (i.e. the 2 with single occurence)?
Here, I would like to get 1 and 8.
So far is what I have:
SELECT node_id
FROM
( SELECT node_id, COUNT(*)
FROM unnest(array[1, 6, 6 , 3, 3 , 8 , 4 ,4]) AS node_id
GROUP BY node_id
) foo
ORDER BY count LIMIT 2;

You are very close, I think:
SELECT node_id
FROM (SELECT node_id, COUNT(*)
FROM unnest(array[1, 6, 6 , 3, 3 , 8 , 4 ,4]) AS node_id
GROUP BY node_id
HAVING count(*) = 1
) foo ;
You can group these back into an array, if you like, using array_agg().

Related

Select rows with second highest value for each ID repeated multiple times

Id values
1 10
1 20
1 30
1 40
2 3
2 9
2 0
3 14
3 5
3 7
Answer should be
Id values
1 30
2 3
3 7
I tried as below
Select distinct
id,
(select max(values)
from table
where values not in(select ma(values) from table)
)
You need the row_number window function. This adds a column with a row count for each group (in your case the ids). In a subquery you are able to ask for the second row of each group.
demo:db<>fiddle
SELECT
id, values
FROM (
SELECT
*,
row_number() OVER (PARTITION BY id ORDER BY values DESC)
FROM
table
) s
WHERE row_number = 2

SQL Renumbering index after group by

I have the following input table:
Seq Group GroupSequence
1 0
2 4 A
3 4 B
4 4 C
5 0
6 6 A
7 6 B
8 0
Output table is:
Line NewSeq GroupSequence
1 1
2 2 A
3 2 B
4 2 C
5 3
6 4 A
7 4 B
8 5
The rules for the input table are:
Any positive integer in the Group column indicates that the rows are grouped together. The entire field may be NULL or blank. A null or 0 indicates that the row is processed on its own. In the above example there are two groups and three 'single' rows.
the GroupSequence column is a single character that sorts within the group. NULL, blank, 'A', 'B' 'C' 'D' are the only characters allowed.
if Group has a positive integer, there must be alphabetic character in GroupSequence.
I need a query that creates the output table with a new column that sequences as shown.
External apps needs to iterate through this table in either Line or NewSeq order(same order, different values)
I've tried variations on GROUP BY, PARTITION BY, OVER(), etc. WITH no success.
Any help much appreciated.
Perhaps this will help
The only trick here is Flg which will indicate a new Group Sequence (values will be 1 or 0). Then it is a small matter to sum(Flg) via a window function.
Edit - Updated Flg method
Example
Declare #YourTable Table ([Seq] int,[Group] int,[GroupSequence] varchar(50))
Insert Into #YourTable Values
(1,0,null)
,(2,4,'A')
,(3,4,'B')
,(4,4,'C')
,(5,0,null)
,(6,6,'A')
,(7,6,'B')
,(8,0,null)
Select Line = Row_Number() over (Order by Seq)
,NewSeq = Sum(Flg) over (Order By Seq)
,GroupSequence
From (
Select *
,Flg = case when [Group] = lag([Group],1) over (Order by Seq) then 0 else 1 end
From #YourTable
) A
Order By Line
Returns
Line NewSeq GroupSequence
1 1 NULL
2 2 A
3 2 B
4 2 C
5 3 NULL
6 4 A
7 4 B
8 5 NULL

TSQL recursive CTE order

I am having trouble figuring out how I can use recursive CTEs to order my results recursively. Here is what I mean (this is a simplified dataset):
I have this as input:
declare #sections table (id int, parent int);
insert into #sections values (1, 1);
insert into #sections values (2, 2);
insert into #sections values (3, 2);
insert into #sections values (4, 2);
insert into #sections values (5, 4);
insert into #sections values (6, 1);
insert into #sections values (7, 6);
insert into #sections values (8, 6);
insert into #sections values (9, 6);
insert into #sections values (10, 9);
-- hierarchical view
--1
-- 6
-- 7
-- 8
-- 10
-- 9
--2
-- 3
-- 4
-- 5
And I want this as output
EDIT: The order of the rows is the important part here
-- id parent depth
-- 1 1 0
-- 6 1 1
-- 7 6 2
-- 8 6 2
-- 10 8 3
-- 9 6 2
-- 2 2 0
This is the best I can do:
with section_cte as
(
select id, parent, 0 'depth' from #sections where id = parent
union all
select cte.id, cte.parent, depth + 1
from #sections s join section_cte cte on s.parent = cte.id where s.id <> s.parent
)
select *from section_cte
Can anyone please help me tweak this query to get what I need?
Thanks!
You missed the part where you need to identify the depth is from the cte
WITH CTE AS (
SELECT
id
, parent
, 0 AS depth
FROM
#sections
WHERE
id=parent
UNION ALL
SELECT
s.id
, s.parent
, c.depth + 1
FROM
#sections s
JOIN CTE c ON s.parent=c.id AND s.id <> s.parent
)
SELECT *
FROM CTE;
During the recursive build, it is a small matter to add a sequence. In the example below, the order is driven by the alphabetical order of the title, but you can use any other available key/sequence.
Declare #Table table (ID int,Pt int,Title varchar(50))
Insert into #Table values (0,null,'Tags'),(1,0,'Transportation'),(2,1,'Boats'),(3,1,'Cars'),(4,1,'Planes'),(5,1,'Trains'),(6,0,'Technology'),(7,6,'FTP'),(8,6,'HTTP'),(9,0,'Finance'),(10,9,'FTP'),(11,9,'401K'),(12,2,'Sail'),(13,2,'Powered'),(14,6,'Internet'),(15,6,'Database'),(16,15,'SQL Server'),(17,15,'MySQL'),(18,15,'MS Access')
Declare #Top int = null --<< Sets top of Hier Try 9
Declare #Nest varchar(25) =' ' --<< Optional: Added for readability
;with cteHB (Seq,ID,Pt,Lvl,Title) as (
Select Seq = cast(1000+Row_Number() over (Order by Title) as varchar(500))
,ID
,Pt
,Lvl=1
,Title
From #Table
Where IsNull(#Top,-1) = case when #Top is null then isnull(Pt,-1) else ID end
Union All
Select Seq = cast(concat(cteHB.Seq,'.',1000+Row_Number() over (Order by cteCD.Title)) as varchar(500))
,cteCD.ID
,cteCD.Pt,cteHB.Lvl+1
,cteCD.Title
From #Table cteCD
Join cteHB on cteCD.Pt = cteHB.ID)
,cteR1 as (Select Seq,ID,R1=Row_Number() over (Order By Seq) From cteHB)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select Hier='HierName'
,B.R1
,C.R2
,A.ID
,A.Pt
,A.Lvl
,Title = Replicate(#Nest,A.Lvl) + A.Title
--,A.Seq --<< Normally Excluded, but you can see how the sequence is built
From cteHB A
Join cteR1 B on A.ID=B.ID
Join cteR2 C on A.ID=C.ID
Order By A.Seq --<< Use R1 if Range Keys are used
Returns
Hier R1 R2 ID Pt Lvl Title
HierName 1 19 0 NULL 1 Tags
HierName 2 4 9 0 2 Finance
HierName 3 3 11 9 3 401K
HierName 4 4 10 9 3 FTP
HierName 5 12 6 0 2 Technology
HierName 6 9 15 6 3 Database
HierName 7 7 18 15 4 MS Access
HierName 8 8 17 15 4 MySQL
HierName 9 9 16 15 4 SQL Server
HierName 10 10 7 6 3 FTP
HierName 11 11 8 6 3 HTTP
HierName 12 12 14 6 3 Internet
HierName 13 19 1 0 2 Transportation
HierName 14 16 2 1 3 Boats
HierName 15 15 13 2 4 Powered
HierName 16 16 12 2 4 Sail
HierName 17 17 3 1 3 Cars
HierName 18 18 4 1 3 Planes
HierName 19 19 5 1 3 Trains
Now, you may have noticed R1 and R2. These are my range keys, and are often used to aggregate data without recursion. If you don't need or want these, just remove the cteR1 and cteR2 (and the corresponding references in the final SELECT).
EDIT
You also have the option of selecting a portion of the hierarchy (i.e. Technology and its children).
The trick you need is to create a sortstring which match your business rule. Something like this.
WITH CTE AS (
SELECT
id, parent, 0 AS depth
--For MS SQL Server 2012+
,cast(format(id,'0000') as varchar(max)) sort
--For previous versions
,cast(stuff('0000',5-len(cast(id as varchar)),len(cast(id as varchar)),id) as varchar(max)) sort1
FROM #sections
WHERE id=parent
UNION ALL
SELECT s.id, s.parent, c.depth + 1
,sort + cast(format(s.id,'0000') as varchar(max)) sort
,sort + cast(stuff('0000',5-len(cast(s.id as varchar)),len(cast(s.id as varchar)),s.id) as varchar(max)) sort1
FROM #sections s
inner join CTE c ON s.parent=c.id AND s.id <> s.parent
)
SELECT *
FROM CTE
order by sort --or by sort1 depending on version

How to optimize query

I have the same problem as mentioned in In SQL, how to select the top 2 rows for each group. The answer is working fine. But it takes too much time. How to optimize this query?
Example:
sample_table
act_id: act_cnt:
1 1
2 1
3 1
4 1
5 1
6 3
7 3
8 3
9 4
a 4
b 4
c 4
d 4
e 4
Now i want to group it (or using some other ways). And i want to select 2 rows from each group. Sample Output:
act_id: act_cnt:
1 1
2 1
6 3
7 3
9 4
a 4
I am new to SQL. How to do it?
The answer you linked to uses an inefficient workaround for MySQL's lack of window functions.
Using a window function is most probably much faster as you only need to read the table once:
select name,
score
from (
select name,
score,
dense_rank() over (partition by name order by score desc) as rnk
from the_table
) t
where rnk <= 2;
SQLFiddle: http://sqlfiddle.com/#!15/b0198/1
Having an index on (name, score) should speed up this query.
Edit after the question (and the problem) has been changed
select act_id,
act_cnt
from (
select act_id,
act_cnt,
row_number() over (partition by act_cnt order by act_id) as rn
from sample_table
) t
where rn <= 2;
New SQLFiddle: http://sqlfiddle.com/#!15/fc44b/1

How to read all records recursively and show by level depth TSQL

Is there a way to read records recursively in similar table and order by depth level?
#table:
id int | parent int | value string
--------------------------------------------
1 -1 some
2 1 some2
3 2 some3
4 2 some4
5 3 some5
6 4 some6
7 3 some5
8 3 some5
9 8 some5
10 8 some5
So is there a way to recursively select where result table would look like this.
select * from #table where id=3
id int | parent int | value string | depth
--------------------------------------------------------
3 2 some3 0
5 3 some5 1
7 3 some5 1
8 3 some5 1
9 8 some5 2
10 8 some5 2
So if I choose id=3 I would see recursion for id=3 and children
Thank you
;with C as
(
select id,
parent,
value,
0 as depth
from YourTable
where id = 3
union all
select T.id,
T.parent,
T.value,
C.depth + 1
from YourTable as T
inner join C
on T.parent = C.id
)
select *
from C
SE-Data
You can accomplish using CTEs, in particular rCTEs.
See this, and this for more information.
Example to follow:
WITH sampleCTE (id, parent, value, depth)
AS (
-- Anchor definition
SELECT id
, parent
, value
, 0
FROM #table
WHERE id = #targetId
-- Recursive definition
UNION ALL
SELECT child.id
, child.parent
, child.value
, sampleCTE.depth + 1
FROM #table child
INNER JOIN sampleCTE ON sampleCTE.id = child.parent
)