How to insert the next highest number - sql

I have a table with an id, tableid, and seqnum. Here is the table structure:
create table ztables
(
id serial primary key,
tableid integer,
seqnum integer
)
and sample data
+----+---------+-------+
| id | tableid | seqnum|
+----+---------+-------+
| 1 | 5 | 1 |
+----+---------+-------+
| 2 | 5 | 2 |
+----+---------+-------+
| 3 | 5 | 3 |
+----+---------+-------+
| 4 | 5 | 9 |
+----+---------+-------+
| 5 | 6 | 1 |
+----+---------+-------+
| 6 | 7 | 1 |
+----+---------+-------+
| 7 | 7 | 2 |
+----+---------+-------+
| 8 | 7 | 3 |
+----+---------+-------+
Lets take tableid 5 as an example. You can see the sequence number increases similar to a database sequence. But it should not increase for the whole table. I only want it to increase per tableid. So, if another record with tableid 5 is inserted the seqnum will be 10. If a record with tableid 7 is inserted the seqnum will be 4. What is the right way to do something like this. I need to account for concurrency issues as well.

Maybe that helps you or gives you at least an idea for a solution:
insert into ztables (
id,
tableid,
seqnum
)
select ? as id,
tableid,
max(seqnum) + 1 as seqnum
from ztables
where tableid = ?
group
by tableid
union
select ? as id,
? as tableid,
1 as seqnum
from ztables
order
by 3 desc
limit 1;
The question marks has to be replaced by constant values.

Related

Get some values from the table by selecting

I have a table:
| id | Number |Address
| -----| ------------|-----------
| 1 | 0 | NULL
| 1 | 1 | NULL
| 1 | 2 | 50
| 1 | 3 | NULL
| 2 | 0 | 10
| 3 | 1 | 30
| 3 | 2 | 20
| 3 | 3 | 20
| 4 | 0 | 75
| 4 | 1 | 22
| 4 | 2 | 30
| 5 | 0 | NULL
I need to get: the NUMBER of the last ADDRESS change for each ID.
I wrote this select:
select dh.id, dh.number from table dh where dh =
(select max(min(t.history)) from table t where t.id = dh.id group by t.address)
But this select not correctly handling the case when the address first changed, and then changed to the previous value. For example id=1: group by return:
| Number |
| -------- |
| NULL |
| 50 |
I have been thinking about this select for several days, and I will be happy to receive any help.
You can do this using row_number() -- twice:
select t.id, min(number)
from (select t.*,
row_number() over (partition by id order by number desc) as seqnum1,
row_number() over (partition by id, address order by number desc) as seqnum2
from t
) t
where seqnum1 = seqnum2
group by id;
What this does is enumerate the rows by number in descending order:
Once per id.
Once per id and address.
These values are the same only when the value is 1, which is the most recent address in the data. Then aggregation pulls back the earliest row in this group.
I answered my question myself, if anyone needs it, my solution:
select * from table dh1 where dh1.number = (
select max(x.number)
from (
select
dh2.id, dh2.number, dh2.address, lag(dh2.address) over(order by dh2.number asc) as prev
from table dh2 where dh1.id=dh2.id
) x
where NVL(x.address, 0) <> NVL(x.prev, 0)
);

Selecting the first row of group with additional group by columns

Say I have a table with the following results:
How is it possible for me to select such that I only want distinct parent_ids with the min result of object0_behaviour?
Expected output:
parent_id | id | object0_behaviour | type
------------------------------------------
1 | 1 | 5 | IP
2 | 3 | 5 | IP
3 | 5 | 7 | ID
4 | 6 | 7 | ID
5 | 8 | 5 | IP
6 | 18 | 7 | ID
7 | 10 | 7 | ID
8 | 9 | 5 | IP
I have tried:
SELECT parent_id, min(object0_behaviour) FROM table GROUP BY parent_id
It works, however if I wanted the other 2 additional columns, I am required to add into GROUP BY clause and things go back to square one.
I saw examples with R : Select the first row by group
Similar output from what I need, but I can't seem to convert it into SQL
You can try using row_number() window function
select * from
(
select *, row_number() over(partition by parent_id order by object0_behaviour) as rn
from tablename
)A where rn=1
select * from table
join (
SELECT parent_id, min(object0_behaviour) object0_behaviour
FROM table GROUP BY parent_id
) grouped
on grouped.parent_id = table.parent_id
and grouped.object0_behaviour = table.object0_behaviour

SQL, How to order the result of a query with precedence constraints

I have two tables with a relation 1-n. I'm looking for the way to order my elements. I could use a column "position(int)" but i prefer to use the id of the precedence :
+-------+---------------+-------+
| pk_id | precedence_id | fk_id |
+-------+---------------+-------+
| 4 | 1 | 10 |
+-------+---------------+-------+
| 1 | 6 | 10 |
+-------+---------------+-------+
| 2 | 5 | 40 |
+-------+---------------+-------+
| 3 | NULL | 10 |
+-------+---------------+-------+
| 6 | 3 | 10 |
+-------+---------------+-------+
| 5 | NULL | 40 |
+-------+---------------+-------+
I have a primary key (pk_id), a foreign key (fk_id) and a precedence constraint on the same table (precedence_id).
I am looking for the query to get the result with the precedence :
+-------+---------------+-------+
| pk_id | precedence_id | fk_id |
+-------+---------------+-------+
| 3 | NULL | 10 |
+-------+---------------+-------+
| 6 | 3 | 10 |
+-------+---------------+-------+
| 1 | 6 | 10 |
+-------+---------------+-------+
| 4 | 1 | 10 |
+-------+---------------+-------+
| 5 | NULL | 40 |
+-------+---------------+-------+
| 2 | 5 | 40 |
+-------+---------------+-------+
SELECT *
FROM tb
ORDER BY fk_id, ??
This is working properly :
WITH RECURSIVE recursive(pk_id, precedence_id, position) AS (
SELECT pk_id, precedence_id, 0
FROM tb
WHERE precedence_id ISNULL
UNION ALL
SELECT v.pk_id, v.precedence_id, rec.position + 1
FROM
tb v
INNER JOIN recursive rec ON rec.pk_id = v.precedence_id
)
SELECT tst.*, rec.position
FROM
recursive rec
INNER JOIN tb tst ON rec.pk_id = tst.pk_id
ORDER BY tst.fk_id, rec.position;
with recursive t (pk_id,path,L_name) as (
select pk_id
,array[pk_id]
,coalesce(precedence_id,0) as L_tree -- can be representative relation_name
,precedence_id
,fk_id
from tb
where coalesce(precedence_id,0) = 0
union all
select el.pk_id
,t.path || array[el.pk_id]
,coalesce(el.precedence_id,0) as L_tree -- can be representative relation_name
,coalesce(el.precedence_id,0) as precedence_id
,el.fk_id
from tb as el
join t on (el.precedence_id= t.pk_id)
where el.pk_id <> any (t.path))
select pk_id
,cast(case when array_upper(path,1)>1 then ' ' else '' end || repeat(' ', array_upper(path,1)-2)||L_name as character varying(1000)) as L_tree
,precedence_id
,array_upper(path,1) as tree_level
,fk_id from t
order by path
Try using recursive with.
I dug this up from one of my old projects.
Edit:
Found a similar example hierarchical data in postgres
use precedence_id in order by clause
select * from tb
order by fk_id,precedence_id desc,pk_id
http://sqlfiddle.com/#!17/ba4b8/5

Efficient ROW_NUMBER increment when column matches value

I'm trying to find an efficient way to derive the column Expected below from only Id and State. What I want is for the number Expected to increase each time State is 0 (ordered by Id).
+----+-------+----------+
| Id | State | Expected |
+----+-------+----------+
| 1 | 0 | 1 |
| 2 | 1 | 1 |
| 3 | 0 | 2 |
| 4 | 1 | 2 |
| 5 | 4 | 2 |
| 6 | 2 | 2 |
| 7 | 3 | 2 |
| 8 | 0 | 3 |
| 9 | 5 | 3 |
| 10 | 3 | 3 |
| 11 | 1 | 3 |
+----+-------+----------+
I have managed to accomplish this with the following SQL, but the execution time is very poor when the data set is large:
WITH Groups AS
(
SELECT Id, ROW_NUMBER() OVER (ORDER BY Id) AS GroupId FROM tblState WHERE State=0
)
SELECT S.Id, S.[State], S.Expected, G.GroupId FROM tblState S
OUTER APPLY (SELECT TOP 1 GroupId FROM Groups WHERE Groups.Id <= S.Id ORDER BY Id DESC) G
Is there a simpler and more efficient way to produce this result? (In SQL Server 2012 or later)
Just use a cumulative sum:
select s.*,
sum(case when state = 0 then 1 else 0 end) over (order by id) as expected
from tblState s;
Other method uses subquery :
select *,
(select count(*)
from table t1
where t1.id < t.id and state = 0
) as expected
from table t;

SQL distinct/groupby on combination of columns

I am trying to do a SQL select on a table based on two columns, but not in the usual way where the combination of values in both columns must be unique; I want to select where the value can only appear once in either column.
Given the dataset:
|pkid | fkself | otherData |
|-----+--------+-----------|
| 1 | 4 | there |
| 4 | 1 | will |
| 3 | 6 | be |
| 2 | 5 | other |
| 5 | 2 | data |
| 6 | 3 | columns |
I need to return either
|pkid | fkself | otherData |
|-----+--------+-----------|
| 1 | 4 | there |
| 3 | 6 | be |
| 2 | 5 | other |
or
|pkid | fkself | otherData |
|-----+--------+-----------|
| 4 | 1 | will |
| 5 | 2 | data |
| 6 | 3 | columns |
The only way I can think of to do this is to concatenate `pkid and fkid in order so that both row 1 and row 2 would concatenate to 1,4, but I'm not sure how to do that, or if it is even possible.
The rows will have other data columns, but it does not matter which row I get, only that I get each ID only once, whether the value is in pkid or fkself.
You can use least and greatest to get the smallest or biggest value of the two. That allows you to put them in the right order to generate those keys for you. You could concatenate the values as you suggested, but it's not needed in this solution. With dense_rank you can generate a sequence for each of those fictional keys. Then, you can get the first OtherData from that sequence.
select
pkid,
fkself,
otherData
from
(select
pkid,
fkself,
otherData,
dense_rank() over (partition by least(pkid, fkself), greatest(pkid, fkself) order by pkid) as rank
from
YourTable t)
where
rank = 1
Your idea is possible, and it should produce the results you want.
SELECT DISTINCT joinedID
FROM (
SELECT min(id) & "," & max(id) as joinedID
FROM (
SELECT pkid as id, someUniqueValue
FROM table
UNION ALL
SELECT fkself as id, someUniqueValue
FROM table)
GROUP BY someUniqueValue )
This will give you a unique list of IDs, concatenated as you like. You can easily include other fields by adding them to each SELECT statement. Also, someUniqueValue can be either an existing unique field, a new unique field, or the concatenated pkid and fkself, if that combination is unique.
The only way I can think of to do this is to concatenate `pkid and
fkid in order so that both row 1 and row 2 would concatenate to 1,4,
but I'm not sure how to do that, or if it is even possible.
You could do it using a CASE statement in Oracle:
SQL> SELECT * FROM sample
2 /
PKID FKSELF
---------- ----------
1 4
4 1
3 6
2 5
5 2
7 7
6 rows selected.
SQL> l
1 SELECT DISTINCT *
2 FROM (
3 SELECT CASE WHEN pkid <= fkself THEN pkid||','||fkself
4 ELSE fkself||','||pkid
5 END "JOINED"
6 FROM sample
7* )
SQL> /
JOINED
-------------------------------------------------------------------------------
1,4
2,5
3,6
7,7