SQL Insert Query For Multiple Max IDs - sql

Table w:
|ID|Comment|SeqID|
|1 |bajg | 1 |
|1 |2423 | 2 |
|2 |ref | 1 |
|2 |comment| 2 |
|2 |juk | 3 |
|3 |efef | 1 |
|4 | hy | 1 |
|4 | 6u | 2 |
How do I insert a standard new comment for each ID for a new SeqID (SeqID increase by 1)
The Below query results in the highest SeqID:
Select *
From w
Where SEQID =
(select max(seqid)
from w)
Table w:
|2 |juk | 3 |
Expected Result
Table w:
|ID|Comment|SeqID|
|1 |sqc | 3 |
|2 |sqc | 4 |
|3 |sqc | 2 |
|4 |sqc | 3 |
Will I have to go through and insert all the values (new comment as sqc) I want into the table using the below, or is there a faster way?
INSERT INTO table_name
VALUES (value1,value2,value3,...);

Try this:
INSERT INTO mytable (ID, Comment, SeqID)
SELECT ID, 'sqc', MAX(SeqID) + 1
FROM mytable
GROUP BY ID
Demo here

You are probably better off just calculating the value when you query. Define an identity column on the table, say CommentId and run a query like:
select id, comment,
row_number() over (partition by comment order by CommentId) as SeqId
from t;
What is nice about this approach is that the ids are always sequential, you don't have no opportunities for duplicates, the table does not have to be locked to when inserting, and the sequential ids work even for updates and deletes.

Related

Sql: Join separately ordered tables

Let's assume I have two sets of events:
Foo
Bar
where I would always expect Bar to follow Foo: Foo -> Bar. I have a table of Foo values:
|----|---------------|------|
| id | ordering-foo | other|
|----|---------------|------|
|1 |1 |X |
|1 |2 |Y |
|----|---------------|------|
|2 |1 |X |
|----|---------------|------|
|3 |2 |X |
|----|---------------|------|
|4 |1 |X |
|4 |2 |Y |
|----|---------------|------|
the ordering field indicates the order at which the Foo events happened per id.
I also have a set of Bar events:
|----|---------------|-------|
| id | ordering_bar | other |
|----|---------------|-------|
|1 |A |XX |
|1 |B |YY |
|----|---------------|-------|
|3 |B |XX |
|----|---------------|-------|
|4 |A |XX |
|----|---------------|-------|
Note that:
while Foo and Bar are both ordered, they don't share the same ordering and we can't simply join them on the said ordering values. Here I have simplified them to numbers vs strings. In the problem that inspired this question, these are the timestamps for each Foo/Bar event respectively, which has the property of foo.ordering < bar.ordering for a Foo->Bar sequence of events, but that's probably not massively helpful to this problem.
The ordering isn't "???", ie just because we have an order entry of 2(B) doesn't mean we'd necessarily have a 1(A) entry. see entries for id: 3
It's possible for us to have a record for Foo but not the subsequent Bar, ie see entries for id: 2, 4
I want to end up with:
|----|----------|-----------|-----------|
| id | ordering | other-foo | other-bar |
| 1 | 1 | X | XX |
| 1 | 2 | Y | YY |
|----|----------|-----------|-----------|
| 2 | 1 | X | null |
|----|----------|-----------|-----------|
| 3 | 2 | X | XX |
|----|----------|-----------|-----------|
| 4 | 1 | X | XX |
| 4 | 2 | Y | null |
|----|----------|-----------|-----------|
How can I get there? In my special case of this problem I only ever have two possible events per event type, per id. ie the ordering values can only ever be: 1,2 / A,B I played around with things like:
case
when count(*) over (partition by foo.id) = 1 and count(*) over (partition by bar.id) = 1 then foo.ordering_foo
when count(*) over (partition by foo.id) = 2 and count(*) over (partition by bar.id) = 1 then 1
when count(*) over (partition by foo.id) = 2 and count(*) over (partition by bar.id) = 2 and max(bar.ordering_bar) over (partition by bar.id) = bar.ordering_bar then 2
when count(*) over (partition by foo.id) = 2 and count(*) over (partition by bar.id) = 2 and min(bar.ordering_bar) over (partition by bar.odering_bar)= bar.ordering_bar then 1
else -1
end as ordering,
ie, I treat each case of:
1 foo, 1 bar
2 foo, 1 bar
2 foo, 2 bar
separately to com up with a composite order. Tho it is likely error-prone, and most importantly I realise this is:
horrible to read/maintain
not flexible enough.
hard to use to get other fields.
So I'm curious if you could solve this more elegantly in the generic case.
You may join the tables using ROW_NUMBER as the following:
SELECT T.id ,T.ordering_foo, T.other other_foo, D.other other_bar
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY ordering_foo) foo_rn
FROM foo
) T
LEFT JOIN
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY ordering_bar) bar_rn
FROM bar
) D
ON T.ID=D.ID AND T.foo_rn=D.bar_rn
ORDER BY T.id ,T.ordering_foo
See a demo on SQL Server.

How to join two queries with count?

I have 3 tables in database:
Table 1: violation
| violation_id | violation_name |
|:-------------:|:--------------:|
|1 | No Parking |
|2 | Speed Contest |
|3 | No Helmet |
Table 2: violators
| violator_id | violation_id |
|:-------------:|:--------------:|
|1 |1 |
|2 |1 |
|3 |3 |
Table 2: previous_violator
| prev_violator_id| violation_id |
|:---------------:|:--------------:|
|1 |1 |
|2 |2 |
|3 |2 |
This view that I want:
| violation_name | Total |
|:-------------:|:--------------:|
|No Parking | 3 |
|Speed Contest | 2 |
|No Helmet | 1 |
I perform this code that joins the violator table and violation:
SELECT *,count(violators.violation_id) as vid
FROM violators
LEFT JOIN violation ON violation.violation_id = violators.violation_id
LEFT JOIN previous_violator ON previous_violator.violator_id = violators.violator_id
WHERE date_apphrehend BETWEEN '$from' AND '$to'
GROUP BY violators.violation_id
My problem is, I want to join the previous violator table that count to the total based on the violation_name.
You can first union all to get them into a single result and then count(*) it. Finally join with Violation to get names. ie:
select violation_name, count(*) as cnt
from (select violation_id from Violators
union all
select violation_id from previous_Violators) tmp
inner join Violation on tmp.violation_id = Violation.violation_id
group by Violation.violation_id, violation_name;
Sample DBFiddle demo.
PS: Sample is in postgreSQL but it would be the same for most backends. You didn't tag your backend.

Update a column value within a SELECT query

I have a complicated SQL question.
Can we update a column within a SELECT query? Example:
Consider this table:
|ID |SeenAt |
----------------
|1 |20 |
|1 |21 |
|1 |22 |
|2 |70 |
|2 |80 |
I want a SELECT Query that gives for each ID when was it seen for the first time. And when did it seen 'again':
|ID |Start |End |
---------------------
|1 |20 |21 |
|1 |20 |22 |
|1 |20 |22 |
|2 |70 |80 |
|2 |70 |80 |
First, both columns Start and End would have the same value, but when a second row with the same ID is seen we need to update its predecessor to give End the new SeenAt value.
I succeeded to create the Start column, I give the minimum SeenAt value per ID to all IDs. But I can't find a way to update the End column everytime.
Don't mind the doubles, I have other columns that change in every new row
Also, I am working in Impala but I can use Oracle.
I hope that I have been clear enough. Thank you
You could use lead() and nvl():
select id, min(seenat) over (partition by id) seen_start,
nvl(lead(seenat) over (partition by id order by seenat), seenat) seen_end
from t
demo
Start is easy just the MIN of the GROUP
End you need to find the MIN after the SeenAt and in case you don't find it then the current SeenAt
SQL DEMO
SELECT "ID",
(SELECT MIN("SeenAt")
FROM Table1 t2
WHERE t1."ID" = t2."ID") as "Start",
COALESCE(
(SELECT MIN("SeenAt")
FROM Table1 t2
WHERE t1."ID" = t2."ID"
AND t1."SeenAt" < t2."SeenAt")
, t1."SeenAt"
) as End
FROM Table1 t1
OUTPUT
| ID | START | END |
|----|-------|-----|
| 1 | 20 | 21 |
| 1 | 20 | 22 |
| 1 | 20 | 22 |
| 2 | 70 | 80 |
| 2 | 70 | 80 |
you seem to need min() analytic function with a self-join:
select distinct t1.ID,
min(t1.SeenAt) over (partition by t1.ID order by t1.ID) as "Start",
t2.SeenAt as "End"
from tab t1
join tab t2 on t1.ID=t2.ID and t1.SeenAt<=t2.SeenAt
order by t2.SeenAt;
Demo

Max value from joined table

I have two tables:
Operations (op_id,super,name,last)
Orders (or_id,number)
Operations:
+--------------------------------+
|op_id| super| name | last|
+--------------------------------+
|1 1 OperationXX 1 |
|2 1 OperationXY 2 |
|3 1 OperationXC 4 |
|4 1 OperationXZ 3 |
|5 2 OperationXX 1 |
|6 3 OperationXY 2 |
|7 4 OperationXC 1 |
|8 4 OperationXZ 2 |
+--------------------------------+
Orders:
+--------------+
|or_id | number|
+--------------+
|1 2UY |
|2 23X |
|3 xx2 |
|4 121 |
+--------------+
I need query to get table:
+-------------------------------------+
|or_id |number |max(last)| name |
|1 2UY 4 OperationXC|
|2 23X 1 OperationXX|
|3 xx2 2 OperationXY|
|4 121 2 OperationXZ|
+-------------------------------------+
use corelared subquery and join
select o.*,a.last,a.name from
(
select super,name,last from Operations from operations t
where last = (select max(last) from operations t2 where t2.super=t.super)
) a join orders o on t1.super =o.or_id
you can use row_number as well
with cte as
(
select * from
(
select * , row_number() over(partition by super order by last desc) rn
from operations
) tt where rn=1
) select o.*,cte.last,cte.name from Orders o join cte on o.or_id=cte.super
SELECT Orders.or_id, Orders.number, Operations.name, Operations.last AS max
FROM Orders
INNER JOIN Operations on Operations.super = Orders.or_id
GROUP BY Orders.or_id, Orders.number, Operations.name;
I don't have a way of testing this right now, but I think this is it.
Also, you didn't specify the foreign key, so the join might be wrong.

increment a variable foreach record

I have this query :
#create table #tmp_table( n_progressive int , name char(10),
id_numeric(11,0) )
declare #i int = 0 declare #c int declare n_progressive int = 0
declare #var_table table ( name char(10), id_number numeric(11,0) )
insert into #var_table( name, id_number ) select name,id_number from MainTable
select #c= count (*) from #var_table
while(#i<#c) begin set #n_progressive = #n_progressive + 1
insert into #Tmptable( n_progressive , name , id_numeric ) select #n_progressive ,name,id_numeric from #var_table
end
The records in var_table are 4. and for each record I want the n_progressive to be incremented +1.
The result of the query above is this :
+--------------+----------+------------+
|n_progressive | name | numeric_id |
+--------------+----------+------------+
|1 | RM1 | 1 |
|1 | RM2 | 2 |
|1 | RM3 | 3 |
|1 | RM4 | 4 |
|2 | RM1 | 1 |
|2 | RM2 | 2 |
|2 | RM3 | 3 |
|2 | RM4 | 4 |
|3 | RM1 | 1 |
|3 | RM2 | 2 |
|3 | RM3 | 3 |
|3 | RM4 | 4 |
|4 | RM1 | 1 |
|4 | RM2 | 2 |
|4 | RM3 | 3 |
|4 | RM4 | 4 |
+--------------+----------+------------+
What I want is this :
+---------------+----------+-------------+
|n_progressive | name | numeric_id |
+---------------+----------+-------------+
|1 | RM1 | 1 |
|2 | RM2 | 2 |
|3 | RM3 | 3 |
|4 | RM4 | 4 |
+---------------+----------+-------------+
I don't want to use Cursors.
You are selecting all the records from #var_table in each iteration of the loop, that's why you get all the records times 4 (the count of records in #var_table).
However, you don't need a loop at all, and you should strive to avoid loops any time you are using SQL anyway, since SQL works best with a set based approach and not a procedural approach (For more information, read RBAR: ‘Row By Agonizing Row’ and What is RBAR? How can I avoid it?)
Instead of a loop, you can simply use the row_number() window function to get the n_progressive value:
insert into #Tmptable( n_progressive, name, id_numeric)
select row_number() over(order by name), name, id_numeric
from #var_table
You are not restricting the INSERT to read one row from your source table, you're copying the whole table multiple times. To directly fix what you are trying to do, you should do something like this...
while(#i<#c) begin
set #n_progressive = #n_progressive + 1
insert into
#Tmptable( n_progressive , name , id_numeric )
select
#n_progressive, name, id_numeric
from
#var_table
WHERE
id_number = #i -- Only one row
SET #i = #i + 1 -- Move to the next row
end
A better idea could be to use ROW_NUMBER(), avoiding the need for the loop and much of the other boiler plate code.
insert into
#Tmptable( n_progressive , name , id_numeric )
select
ROW_NUMBER() OVER (ORDER BY id_numeric),
name,
id_numeric
from
#var_table
A better idea still could be to use an identity column, and let the table do the number allocations.
create table
#tmp_table(
n_progressive int IDENTITY(1,1),
name char(10) ,
id_ numeric(11,0)
)
insert into #Tmptable(name , id_numeric )
select name, id_numeric
from MainTable
ORDER BY id_numeric
Does this do what you want?
with n as (
select 1 as n
union all
select n + 1
from n
where n < #n_limit
)
select n.n, name + cast(n.n as varchar(255)), n.n as numeric_id
from n
option (maxrecursion 0);