How to do a batch insert version of auto increment? - sql

I have a temporary table that has columns id and value:
| id | value |
And I have a table that aggregates data with columns g_id and value:
| g_id | value |
The id in the temporary table is locally ordered by id, here's 2 examples:
temp_table 1
| id | value |
+----+--------+
| 1 | first |
| 2 | second |
| 3 | third |
temp_table 2
| id | value |
+----+-------+
| 2 | alpha |
| 3 | beta |
| 4 | gamma |
I want to insert all the rows from the temp table into the global table, while having the g_id ordered globally. If I insert temp table 1 before temp table 2, it should be like this:
global_table
| group_id | value |
+----------+--------+
| 1 | first |
| 2 | second |
| 3 | third |
| 4 | alpha |
| 5 | beta |
| 6 | gamma |
For my purposes, it is ok if there are jumps between consecutive numbers, and if 2 inserts are done at the same time, it can be interleaved. The requirement is basically that the id columns are always increasing.
e.g.
Let's say I have 2 sets of queries run at the same time, and the group_id in global_table is auto incremented:
Query 1:
INSERT INTO global_table (value) VALUES (first)
INSERT INTO global_table (value) VALUES (second)
INSERT INTO global_table (value) VALUES (third)
Query 2:
INSERT INTO global_table (value) VALUES (alpha)
INSERT INTO global_table (value) VALUES (beta)
INSERT INTO global_table (value) VALUES (gamma)
I can get something like this:
global_table
| group_id | value |
+----------+--------+
| 1 | first |
| 3 | alpha |
| 4 | second |
| 5 | third |
| 6 | beta |
| 7 | gamma |
How do I achieve something like this with inserting from a table? Like with
INSERT INTO global_table (value)
SELECT t.value
FROM temp_table t
Unfortunately, this may not result in a incrementing id all the time.

The requirement is basically that the id columns are always
increasing.
If I understood you correctly, you should use ORDER BY in your INSERT statement.
INSERT INTO global_table (value)
SELECT t.value
FROM temp_table t
ORDER BY t.id;
In SQL Server, if you include ORDER BY t.id, it will guarantee that the new IDs generated in the global_table table will be in the specified order.
I don't know about other databases, but SQL Server IDENTITY has such guarantee.
Using your sample data, it is guaranteed that the group_id generated for value second will be greater than the value generated for the value first. And group_id for value third will be greater than for value second.
They may be not consecutive, but if you specify ORDER BY, their relative order will be preserved.
Same for the second table, and even if you run two INSERT statements at the same time. Generated group_ids may interleave between two tables, but relative order within each statement will be guaranteed.
See my answer for a similar question with more technical details and references.

Related

How can you assign all the different values in a table variable to fields in existing rows of a table?

I have a table variable (#tableVar) with one column (tableCol) of unique values.
I have a target table with many existing rows that also has a column that is filled entirely with the NULL value.
What type of statement can I use to iterate through #tableVar and assign a different value from #tableVar.tableCol to the null field for each of the rows in my target table?
*Edit (to provide info)
My Target table has this structure:
+-------+------------+
| Name | CallNumber |
+-------+------------+
| James | NULL |
| Byron | NULL |
| Steve | NULL |
+-------+------------+
My table variable has this structure
+------------+
| CallNumber |
+------------+
| 6348 |
| 2675 |
| 9898 |
+------------+
I need to assign a different call number to each row in the target table, to achieve this kind of result.
+-------+------------+
| Name | CallNumber |
+-------+------------+
| James | 6348 |
| Byron | 2675 |
| Steve | 9898 |
+-------+------------+
Note: Each row does not need a specific CallNumber. The only requirement is that each row have a unique CallNumber. For example, "James" does not specifically need 6348; He can have any number as long as it's unique, and the unique number must come from the table variable. You can assume that the table variable will have enough CallNumbers to meet this requirement.
What type of query can I use for this result?
You can use an update with a sequence number:
with toupdate as (
select t.*, row_number() over (order by (select null)) as seqnum
from target t
)
update toupdate
set col = tv.tablecol
from (select tv.*, row_number() over (order by (select null)) as seqnum
from #tablevar tv
) tv
where tv.seqnum = toupdate.seqnum;
This assumes that #tablevar has a sufficient number of rows to assign in target. If not, I would suggest that you ask a new question with sample data and desired results.
Here is a db<>fiddle.

How to create INSERT query that adds sequence number in one table to another

I have a table sample_1 in a Postgres 10.7 database with some longitudinal research data and an ascending sequence number per key. I need to INSERT data from a staging table (sample_2) maintaining the sequence column accordingly.
sequence numbers are 0-based. I assume I need a query to seek the greatest sequence number per key in sample_1 and add that to each new row's follow-up sequence number. I'm mainly struggling at this step with the sequence number arithmetic. Tried this:
INSERT INTO sample_1 (KEY, SEQUENCE, DATA)
SELECT KEY, sample_2.SEQUENCE + max(sample_1.SEQUENCE), DATA
FROM sample_2;
However, I get errors saying I can't use 'sample_1.SEQUENCE' in Line 2 because that's the table being inserted in to. I can't figure out how to do the arithmetic with my insert sequence!
Sample data:
sample_1
| KEY | SEQUENCE | DATA |
+-------------+----------+------+
| YMH_0001_XX | 0 | a |
| YMH_0001_XX | 1 | b |
| YMH_0002_YY | 0 | c |
sample_2
| KEY | SEQUENCE | DATA |
+-------------+----------+------+
| YMH_0001_XX | 1 | d |
| YMH_0002_YY | 1 | e |
| YMH_0002_YY | 2 | f |
I want to continue ascending sequence numbers per key for inserted rows.
To be clear, the resultant table in this example would be 3 columns and 6 rows as such:
sample_1
| KEY | SEQUENCE | DATA |
+-------------+----------+------+
| YMH_0001_XX | 0 | a |
| YMH_0001_XX | 1 | b |
| YMH_0001_XX | 2 | d |
| YMH_0002_YY | 0 | c |
| YMH_0002_YY | 1 | e |
| YMH_0002_YY | 2 | f |
That should do what you are after:
INSERT INTO sample_1 (key, sequence, data)
SELECT s2.key
, COALESCE(s1.seq_base, -1)
+ row_number() OVER (PARTITION BY s2.key ORDER BY s2.sequence)
, s2.data
FROM sample_2 s2
LEFT JOIN (
SELECT key, max(sequence) AS seq_base
FROM sample_1
GROUP BY 1
) s1 USING (key);
Notes
You need to build on the existing maximum sequence per key in sample_1. (I named it seq_base.) Compute that in a subquery and join to it.
Add row_number() to it as demonstrated. That preserves the order of input rows, discarding absolute numbers.
We need the LEFTJOIN to avoid losing rows with new keys from sample_2.
Likewise, we need COALESCE to start a fresh sequence for new keys. Default to -1 to effectively start sequences with 0 after adding the 1-based row number.
This is not safe for concurrent execution, but I don't think that's your use case.

On SQL insert, write the new row's assigned ID in a second column

Let's say I have the following table
| id | name | anchorId |
|----|--------|----------|
| 1 | Robert | 1 |
| 2 | William| 2 |
| 3 | Bob | 1 |
Basically, let's say a new row gets added
| id | name | anchorId |
|----|--------|----------|
| 1 | Robert | 1 |
| 2 | William| 2 |
| 3 | Bob | 1 |
| 4 | Billy | 4 |
Until I can get to and point set Billy's anchorId to 2, it, I would like it to refer to itself.
Namely, on insert, if unspecified, how can set anchorId to be the newly assigned id?
Some choices (most preferential first IMO)
Set the anchorId to NULL for new inserts instead of to the row ID. This is the usual way of representing this.
Use a sequence instead of an IDENTITY - then you can set the default for both id and anchorId to be from the sequence and both will be assigned the same value.
If using an IDENTITY you will need to do the INSERT then followed by an immediate UPDATE on the newly inserted value (using the IDENTITY value returned from SCOPE_IDENTITY() or the OUTPUT clause or the INSERTED table in a trigger).
Code for 2
CREATE SEQUENCE S1 AS INT START WITH 1;
CREATE TABLE T1
(
id INT NOT NULL DEFAULT NEXT VALUE FOR S1,
name varchar(50),
anchorid INT NOT NULL DEFAULT NEXT VALUE FOR S1
)
INSERT INTO T1 (name) VALUES ('foo');
SELECT *
FROM T1

Join two tables returning all rows as single row from the second table

I want to get data in a single row from two tables which have one to many relation.
Primary table
Secondary table
I know that for each record of primary table secondary table can have maximum 10 rows. Here is structure of the table
Primary Table
-------------------------------------------------
| ImportRecordId | Summary |
--------------------------------------------------
| 1 | Imported Successfully |
| 2 | Failed |
| 3 | Imported Successfully |
-------------------------------------------------
Secondary table
------------------------------------------------------
| ImportRecordId | CodeName | CodeValue |
-------------------------------------------------------
| 1 | ABC | 123456A |
| 1 | DEF | 8766339 |
| 1 | GHI | 887790H |
------------------------------------------------------
I want to write a query with inner join to get data from both table in a way that from secondary table each row should be treated as column instead showing as multiple row.
I can hard code 20 columns names(as maximum 10 records can exist in secondary table and i want to display values of two columns in a single row) so if there are less than 10 records in the secondary table all other columns will be show as null.
Here is expected Output. You can see that for first record in primary table there was only three rows that's why two required columns from these three rows are converted into columns and for all others columns values are null.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| ImportRecordId | Summary | CodeName1 | CodeValue1 | CodeName2 | CodeValue2 | CodeName3 | CodeValue3 | CodeName4 | CodeValue4| CodeName5 | CodeValue5| CodeName6 | CodeValue6| CodeName7 | CodeValue7 | CodeName8 | CodeValue8 | CodeName9 | CodeValue9 | CodeName10 | CodeValue10|
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | Imported Successfully | ABC | 123456A | DEF | 8766339 | GHI | 887790H | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Here is my simple SQL query which return all data from both tables but instead multiple rows from secondary table i want to get them in a single row like above result set.
Select p.ImportRecordId,p.Summary,s.*
from [dbo].[primary_table] p
inner join [dbo].[secondary_table] s on p.ImportRecordId = s.ImportRecordId
The following uses Row_Number(), a JOIN and a CROSS APPLY to create the source of the PIVOT
You'll have to add the CodeName/Value 4...10
Example
Select *
From (
Select A.[ImportRecordId]
,B.Summary
,C.*
From (
Select *
,RN = Row_Number() over (Partition by [ImportRecordId] Order by [CodeName])
From Secondary A
) A
Join Primary B on A.[ImportRecordId]=B.[ImportRecordId]
Cross Apply (values (concat('CodeName' ,RN),CodeName)
,(concat('CodeValue',RN),CodeValue)
) C(Item,Value)
) src
Pivot (max(value) for Item in (CodeName1,CodeValue1,CodeName2,CodeValue2,CodeName3,CodeValue3) ) pvt
Returns
ImportRecordId Summary CodeName1 CodeValue1 CodeName2 CodeValue2 CodeName3 CodeValue3
1 Imported Successfully ABC 123456A DEF 8766339 GHI 887790H

Pivot SSRS Dataset

I have a dataset which looks like so
ID | PName | Node | Val |
1 | Tag | Name | XBA |
2 | Tag | Desc | Dec1 |
3 | Tag | unit | Int |
6 | Tag | tids | 100 |
7 | Tag | post | AAA |
1 | Tag | Name | XBB |
2 | Tag | Desc | Des9 |
3 | Tag | unit | Float |
7 | Tag | post | BBB |
6 | Tag | tids | 150 |
I would like the result in my report to be
Name | Desc | Unit | Tids | Post |
XBA | Dec1 | int | 100 | AAA |
XBB | Des9 | Float | 150 | BBB |
I have tried using a SSRS Matrix with
Row: PName
Data: Node
Value: Val
The results were simply one row with Name and next row with desc and next with unit etc. Its not all in the same rows and also the second row was missing. This is possibly because there is no grouping on the dataset.
What is a good way of achieving the expected results?
I would not recommend this for a production scenario but if you need to knock out a report quickly or something you can try this. I would just not feel comfortable that the order of the records you get will always be what you expect.
You COULD try to insert the results of the SP into a table (regular table, temp table, table variable...doesn't matter really as long as you can get an identity column added). Assuming that the rows always come out in the correct order (which is probably not a valid assumption 100% of the time) then add an identity column on the table to get a unique row number for each row. From there you should be able to write some math logic to "group" your values together and then pivot out what you want.
create table #temp (ID int, PName varchar(100), Node varhar(100), Val varchar(100))
insert #temp exec (your stored proc)
alter table #temp add UniqueID int identity
then use UniqueID (modulo on 5 perhaps?) to group records together and then pivot