find nth max salary from a table without using cte, top, subquery - sql-server

In an interview I was asking to find nth maximum salary without using CTE, top, sub query. I am confused. Is any other way to do.
And another question,
2 tables having identical columns with Identity ID column. When copying records from table1 to table2, the ID values also should be moved. For example, if table1 having the IDs 1,3,5,7.. the same ID values should be moved to table2 ID column.

He is looking for new feature OFFSET.
Query to get 10th salary
SELECT * FROM dbp.Empsalary AS P
ORDER BY P.salary desc
OFFSET 9 ROWS
FETCH NEXT 1 ROWS ONLY
Note: N -1 =9 here
Second : Question he is looking for do you know SET IDENTITY_INSERT
you have to SET IDENTITY_INSERT off for second table.

For the 2nd Problem, It can Be achieved By Turning ON The IDENTITY_INSERT for the Destination Table. Like this
CREATE TABLE dbo.TableA
(
Id INT IDENTITY(1,1),
Val VARCHAR(50)
)
CREATE TABLE dbo.TableB
(
Id INT IDENTITY(1,1),
Val VARCHAR(50)
)
INSERT INTO TableA(Val)
VALUES('ABC'),('HIJ'),('XYZ')
SET IDENTITY_INSERT TableB on
INSERT INTO TableB(Id,Val)
SELECT Id,Val FROM TableA

Related

How to overwrite the table rows by another Table if they are duplicate rows

I have a table in snowflake which has some data like below
Table 1(snowflake table)
LOCATIONID OBSERVATION_TIME_UTC source_record_id Value
LFOB 201001000001.00 cw_altdata:LFOB_historical_hourly.txt:2020-12-23_003400:1 3
LFOB 201001000002.00 cw_altdata:LFOB_historical_hourly.txt:2020-12-23_003400:2 3
and for the existing table I need to append the data and remove the duplicates based on first 2 columns
Table 2(Need to append to the existing table)
LOCATIONID OBSERVATION_TIME_UTC source_record_id Value
LFOB 201001000001.00 cw_altdata:LFOB_historical_hourly.txt:2020-12-24_003400:3 4
LFOB 201001000002.00 cw_altdata:LFOB_historical_hourly.txt:2020-12-24_003400:4 4
after appending the Table 2 data. I want the duplicate data to be removed from table. My output table should be looking this.
LOCATIONID OBSERVATION_TIME_UTC source_record_id Value
LFOB 201001000001.00 cw_altdata:LFOB_historical_hourly.txt:2020-12-24_003400:3 4
LFOB 201001000002.00 cw_altdata:LFOB_historical_hourly.txt:2020-12-24_003400:4 4
Here we can see duplicate rows has been removed. It should keep latest date. for eg: here 2020-12-24_003400 is latest date than previous table 1.
I only know some basics of sql statements. I did not find any articles regarding this, so did not get a chance to try any solutions. It would be a great help if someone has a solution.
UPDATE is an the most expensive DML in Snowflake (and just about every other RDBMS). IF the number of rows in Table 2 is a significant percentages of Table 1 AND a significant percentages will result in UPDATE instead of INSERT, the following technique is an alternative:
DELETE FROM TABLE_1 T1 WHERE (T1.LOCATIONID, T1.OBSERVATION_TIME_UTC)
IN (SELECT T2.LOCATIONID, T2.OBSERVATION_TIME_UTC FROM TABLE_2 T2);
INSERT INTO TABLE_1 (SELECT * FROM TABLE_2);
If you want to eliminate rows duplicate across all columns in the table (and assuming no duplicates in Table_1:
INSERT INTO TABLE_1
(SELECT * FROM TABLE_2
MINUS
SELECT * FROM TABLE_1)
or, if the table has many columns:
INSERT INTO TABLE_1
(SELECT * FROM TABLE_2 T2
WHERE (T2.LOCATIONID, T2.OBSERVATION_TIME_UTC)
NOT IN
( SELECT
LOCATIONID, OBSERVATION_TIME_UTC
FROM TABLE_T2
MINUS
SELECT
LOCATIONID, OBSERVATION_TIME_UTC
FROM TABLE_T1)
You can use a merge statement to update table_1 with the values from table_2 when they are different for the business key (assuming in this case that the business key is LOCATIONID, OBSERVATION_TIME_UTC). If the business key does not exist in table_1 the merge statement will insert the row.
Here is the merge:
merge into table_1
using(SELECT LOCATIONID,
OBSERVATION_TIME_UTC,
source_record_id,
Value
FROM table_2
) table_2
on table_1.LOCATIONID = table_2.LOCATIONID
and table_1.OBSERVATION_TIME_UTC = table_2.OBSERVATION_TIME_UTC
WHEN MATCHED
and table_1.source_record_id is distinct from table_2.source_record_id or
table_1.value is distinct from table_2.value
THEN UPDATE
SET table_1.source_record_id = table_2.source_record_id,
table_1.value = table_2.value
WHEN NOT MATCHED
THEN INSERT
(
LOCATIONID,
OBSERVATION_TIME_UTC,
source_record_id,
Value
)
VALUES
(
table_2.LOCATIONID,
table_2.OBSERVATION_TIME_UTC,
table_2.source_record_id,
table_2.Value
)
;

Copy whole row excluding identifier column

I'm trying to insert a new row into a table which is an exact copy of another row except for the identifier. Previously I hadn't had the issue because there was an ID-column which didn't fill automatically. So I could just copy a row like this
INSERT INTO table1 SELECT * FROM table1 WHERE Id = 5
And then manually change the ID like this
WITH tbl AS (SELECT ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) AS RNr, Id
FROM table1 WHERE Id = 5
UPDATE tbl SET Id = (SELECT MAX(Id) FROM table1) + 1
WHERE RNr = 2
Recently the column Id has changed to not be filled manually (which I also like better). But this leads to the error that I obviously can't fill that column while IDENTITY_INSERT is false. Unfortunately, I don't have the right to use SET IDENTITY_INSERT IdentityTable ON/OFF before and after the statement, which I also would like to avoid anyways.
I'm looking for a way to insert the whole row excluding the Id column without naming each other column in the INSERT and SELECT statement (which are quite a lot).
In the code below the maximum value of the ID gets added by one, so your integrity is not violated.
insert into table1
select a.Top_ID, Column2, Column3, ColumnX
from table1 t1
outer apply (select max(id_column) + 1as Top_ID from table1) as a
where t1.Id = 1
Okay, I found a way by using a temporary table
SELECT * INTO #TmpTbl
FROM table1 WHERE Id = 5;
ALTER TABLE #TmpTbl
DROP COLUMN Id;
INSERT INTO table1 SELECT * FROM #TmpTbl;
DROP TABLE #TmpTbl

Table rows can never be more than 15

I do not want the record to the table more than 15.
Scenario:
A new record is saved. If it were a record number of 16. The first record to be deleted.
How do I remove the first record?Can it be done automatically?
if it is entity framework and you want to use a basic rule here it is
suppose your object is person and its set is called people
Before you do context.people.add(new person()) apply following logic
obtain count of people in database context.people.count()
check if this count is greater than 15 you can do this via single statment if(context.people.count()>15)
inside if you can write people firstperson = context.people.OrderBy(x=>x.ID).First() or if you have date inserted or added you can use.OrderBy(x => x.dateadded)and pick the first element. Make sure you order it in correct way usingOrderByorOrderByDescending`
place this record in a variable and call context.remove(firstperson) before you do context.add(new person())
If you are doing this in an empty table your ID's would increment but you can safely delete by ID order and pick the least one every time you delete.
WITH A AS
(
SELECT TOP 1 *
FROM MyTable
)
DELETE FROM A
The rows referenced in the TOP expression used with INSERT, UPDATE, or DELETE are not arranged in any order.
Therefore, you better use WITH decision with ORDER BY clause, which will let you specify more exactly which row you consider to be the first.
This uses a trigger and an identity column to ensure only the 15 most-recently-inserted rows are kept in the table.
CREATE TABLE MyTable
(
rowID INT IDENTITY(1,1) PRIMARY KEY
,MyColumn VARCHAR(255) NOT NULL
)
GO
CREATE TRIGGER TG_MyTable_Only15
ON MyTable
AFTER INSERT
AS
BEGIN
WITH
t1
(
rowID
)
AS
(
SELECT TOP 15
rowID
FROM MyTable
ORDER BY rowID DESC
)
DELETE FROM MyTable
WHERE rowID NOT IN (SELECT rowID FROM t1)
END
GO

Copy Each Identity Of Inserted Records

I have two table
first:
Table: TBL#Sell
SellId ClientId ProductId
1 3
3 5
4 6
second:
Table: TBL#Sell2
SellId ClientId ProductId
Now I want to copy every record of first table to second one.
"SellId" column in second table (Sell2.SellId) is Auto Increment (Identity).
for any insert the TBL#Sell2.SellId will set with new identity and i must store the identity in TBL#Sell1.SellId
Is it clear?
What is the solution? , plz.
thanks
I want to store TBL#Sell2.SellId in TBL#Sell.SellId
You can use triggers :
http://msdn.microsoft.com/en-us/magazine/cc164047.aspx
http://msdn.microsoft.com/en-us/library/aa258254(v=sql.80).aspx
Look at the OUTPUT clause in Books Online.
It TBL#Sell2 is empty you basically only want to give a number to each row in TBL#Sell. You can do that without using TBL#Sell2.
Add a temporary identity column, move the values to to SellId, remove the temp column.
alter table TBL#Sell add SellId_tmp int not null identity
go
update TBL#Sell set SellId = SellId_tmp
go
alter table TBL#Sell drop column SellId_tmp
Another way is to use a CTE with row_number().
;with cte as
(
select *,
row_number() over(order by (select(1))) as rn
from TBL#Sell
)
update cte set
SellId = rn
I think you have more ways to perform this task.
You could write a stored procedure that for every record in table1 write to table2 and then update table1 getting the identity value from SCOPE_IDENTITY()
Or another way could be, if the couple ClientId/ProductId is a key, to do an insert and then an update with something like that:
insert into TBL#Sell2
select SellId, ClientId, ProductId from TBL#Sell
upadte TBL#Sell
set TBL#Sell.SellId = TBL#Sell2.SellId
from TBL#Sell T1 join TBL#Sell2 T2
on T1.ClientId = T2.ClientId and T1.ProductId = T2.ProductId
EDIT Replaced ##Identity with SCOPE_IDENTITY

What columns can be used in OUTPUT INTO clause?

I'm trying to build a mapping table to associate the IDs of new rows in a table with those that they're copied from. The OUTPUT INTO clause seems perfect for that, but it doesn't seem to behave according to the documentation.
My code:
DECLARE #Missing TABLE (SrcContentID INT PRIMARY KEY )
INSERT INTO #Missing
( SrcContentID )
SELECT cshadow.ContentID
FROM Private.Content AS cshadow
LEFT JOIN Private.Content AS cglobal ON cshadow.Tag = cglobal.Tag
WHERE cglobal.ContentID IS NULL
PRINT 'Adding new content headers'
DECLARE #Inserted TABLE (SrcContentID INT PRIMARY KEY, TgtContentID INT )
INSERT INTO Private.Content
( Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, OrgUnitID )
OUTPUT cglobal.ContentID, INSERTED.ContentID INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, NULL
FROM Private.Content AS cglobal
INNER JOIN #Missing AS m ON cglobal.ContentID = m.SrcContentID
Results in the error message:
Msg 207, Level 16, State 1, Line 34
Invalid column name 'SrcContentID'.
(line 34 being the one with the OUTPUT INTO)
Experimentation suggests that only rows that are actually present in the target of the INSERT can be selected in the OUTPUT INTO. But this contradicts the docs in the books online. The article on OUTPUT Clause has example E that describes a similar usage:
The OUTPUT INTO clause returns values
from the table being updated
(WorkOrder) and also from the Product
table. The Product table is used in
the FROM clause to specify the rows to
update.
Has anyone worked with this feature?
(In the meantime I've rewritten my code to do the job using a cursor loop, but that's ugly and I'm still curious)
You can do this with a MERGE in Sql Server 2008. Example code below:
--drop table A
create table A (a int primary key identity(1, 1))
insert into A default values
insert into A default values
delete from A where a>=3
-- insert two values into A and get the new primary keys
MERGE a USING (SELECT a FROM A) AS B(a)
ON (1 = 0) -- ignore the values, NOT MATCHED will always be true
WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example
OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data
Result is
INSERT, 3, NULL, 1
INSERT, 4, NULL, 2
i.e. for each row the new primary key (3, 4) and the old one (1, 2). Creating a table called e.g. #OUTPUT and adding " INTO #OUTPUT;" at the end of the OUTPUT clause would save the records.
I've verified that the problem is that you can only use INSERTED columns. The documentation seems to indicate that you can use from_table_name, but I can't seem to get it to work (The multi-part identifier "m.ContentID" could not be bound.):
TRUNCATE TABLE main
SELECT *
FROM incoming
SELECT *
FROM main
DECLARE #Missing TABLE (ContentID INT PRIMARY KEY)
INSERT INTO #Missing(ContentID)
SELECT incoming.ContentID
FROM incoming
LEFT JOIN main
ON main.ContentID = incoming.ContentID
WHERE main.ContentID IS NULL
SELECT *
FROM #Missing
DECLARE #Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50))
INSERT INTO main(ContentID, [Content])
OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO #Inserted (ContentID, [Content])
SELECT incoming.ContentID, incoming.[Content]
FROM incoming
INNER JOIN #Missing AS m
ON m.ContentID = incoming.ContentID
SELECT *
FROM #Inserted
SELECT *
FROM incoming
SELECT *
FROM main
Apparently the from_table_name prefix is only allowed on DELETE or UPDATE (or MERGE in 2008) - I'm not sure why:
from_table_name
Is a column prefix that specifies a table included in the FROM clause of a DELETE or UPDATE statement that is used to specify the rows to update or delete.
If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.
I'm running into EXACTLY the same problem as you are, I feel your pain...
As far as I've been able to find out there's no way to use the from_table_name prefix with an INSERT statement.
I'm sure there's a viable technical reason for this, and I'd love to know exactly what it is.
Ok, found it, here's a forum post on why it doesn't work:
MSDN forums
I think I found a solution to this problem, it sadly involves a temporary table, but at least it'll prevent the creation of a dreaded cursor :)
What you need to do is add an extra column to the table you're duplicating records from and give it a 'uniqueidentifer' type.
then declare a temporary table:
DECLARE #tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int)
insert the the data into your temp table like this:
insert into #tmptable
(uniqueid,original_id,new_id)
select NewId(),id,0 from OriginalTable
the go ahead and do the real insert into the original table:
insert into OriginalTable
(uniqueid)
select uniqueid from #tmptable
Now to add the newly created identity values to your temp table:
update #tmptable
set new_id = o.id
from OriginalTable o inner join #tmptable tmp on tmp.uniqueid = o.uniqueid
Now you have a lookup table that holds the new id and original id in one record, for your using pleasure :)
I hope this helps somebody...
(MS) If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.
In your example, you can't use cglobal table in the OUTPUT unless it's INSERTED.column_name or DELETED.column_name:
INSERT INTO Private.Content
(Tag)
OUTPUT cglobal.ContentID, INSERTED.ContentID
INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag
FROM Private.Content AS cglobal
INNER JOIN #Missing AS m ON cglobal.ContentID = m.SrcContentID
What worked for me was a simple alias table, like this:
INSERT INTO con1
(Tag)
OUTPUT **con2**.ContentID, INSERTED.ContentID
INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag
FROM Private.Content con1
**INNER JOIN Private.Content con2 ON con1.id=con2.id**
INNER JOIN #Missing AS m ON con1.ContentID = m.SrcContentID

Resources