If value is X in table1 mark value X in table2 - sql-server

I'm trying to figure out how to make the following scenario work out in SQL Server:
We have two tables in a DB. Table1 contains full information of users with a column called "Tag" marked with values from 1 to 300.
We have another table which contains two columns where one is the number 1 to 300 as well and the second with either 0 or 1 as value.
We'd like that whenever a tag number is used in Table1 it marks it as "0" in Table2 standing for "in use" as 1 means "available".
Anyone who can assist me?

Try this:
DECLARE #tbl1 as TABLE(
Tag INT
)
DECLARE #tbl2 as TABLE(
Id INT,
IsAvailable BIT
)
INSERT INTO #tbl1 VALUES(1)
INSERT INTO #tbl1 VALUES(2)
INSERT INTO #tbl1 VALUES(5)
INSERT INTO #tbl2 VALUES(1,NULL)
INSERT INTO #tbl2 VALUES(2,NULL)
INSERT INTO #tbl2 VALUES(3,NULL)
INSERT INTO #tbl2 VALUES(4,NULL)
INSERT INTO #tbl2 VALUES(5,NULL)
SELECT
T1.Tag,
T2.Id,
CASE ISNULL(T1.Tag,0) WHEN 0 THEN 1 ELSE 0 END AS IsAvailable
FROM #tbl2 T2
LEFT JOIN #tbl1 T1 ON T1.Tag=T2.Id

Related

SQL Server: bulk insert node into xml joined to another table

I've googled but can't find a good example of this.
I have a #temp table with an pk ID and decimal column
ID decimal
2 0.34
3 0.1
I have another table called master having a column with the same pk and an xml column like:
Master
ID xml
2 <Form ....
3 <Form.....
I need to insert a new node into the xml that has its element name as the decimal value. All which have the same element name and at the same level.
The xml on a basic level looks like:
<Form formCode="123">
<Node1>234</Node1>
<Node2>234</Node3>
</Form>
And I want the final xml to look like:
<Form formCode="123">
<Node1>234</Node1>
<Node2>234</Node3>
<NewNode>0.34</NewNode>
</Form>
I think it should be something like:
UPDATE Master
SET
xml.modify('insert /Form/'...followed by some kind of join.
Try something like this
DECLARE #tbl TABLE(ID INT, decimalColumn DECIMAL(4,2));
INSERT INTO #tbl VALUES
(2,0.34)
,(3,0.1);
DECLARE #master TABLE(ID INT, xmlColumn XML);
INSERT INTO #master VALUES
(2,
'<Form formCode="123">
<Node1>234</Node1>
<Node2>234</Node2>
</Form>')
,(3,
'<Form formCode="456">
<Node1>234</Node1>
<Node2>234</Node2>
</Form>')
UPDATE #master SET xmlColumn.modify('insert sql:column("NewNode.AsXml") as last into /Form[1]')
FROM #master AS m
INNER JOIN #tbl AS tbl ON tbl.ID=m.ID
CROSS APPLY(SELECT CAST(tbl.decimalColumn AS VARCHAR(MAX)) FOR XML PATH('NewNode'),TYPE) AS NewNode(AsXml);
SELECT * FROM #master
The result
2 <Form formCode="123"><Node1>234</Node1><Node2>234</Node2><NewNode>0.34</NewNode></Form>
3 <Form formCode="456"><Node1>234</Node1><Node2>234</Node2><NewNode>0.10</NewNode></Form>
Use UPDATE ... FROM and the sql:column function:
DECLARE #temp TABLE (id int, d decimal(10,2));
DECLARE #master TABLE (id int, x xml);
INSERT #temp
VALUEs (2, 0.34),(3,.1);
INSERT #master
VALUES (2, '<Form><Test /></Form>'), (3, '<Form><Test /></Form>')
UPDATE m
SET x.modify('insert <NewNode>{sql:column("d.d")}</NewNode> after (/Form/Test)[1]')
FROM #master m
INNER JOIN #temp d
ON m.id = d.id
SELECT * FROM #master

SQL MERGE INTO - How to delete then insert matched/existing data?

I currently have a stored procedure that compares my target table (Ticket_Report) to my data source table (New_Tickets).
I am using a MERGE INTO statement to compare these two. When it finds a match between the two tables, it updates the current row in the target table with the corresponding info from the source table. If it dosent find a match, it inserts that data from the source table into the target table.
MERGE INTO Ticket_REPORT T1
USING #New_Tickets T2
ON T1.TICKET_NO=T2.TICKET_NO
WHEN MATCHED THEN
UPDATE SET
T1.TICKET_NO = T2.TICKET_NO,
T1.ASSIGNED_GROUP = T2.ASSIGNED_GROUP,
T1.ASSIGNEE = T2.ASSIGNEE,
T1.FNAME = T2.FNAME,
T1.LNAME = T2.LNAME
WHEN NOT MATCHED THEN
INSERT VALUES(
T2.TICKET_NO,
T2.ASSIGNED_GROUP,
T2.ASSIGNEE,
T2.FNAME,
T2.LNAME
);
What I need to do is, when I find a MATCH, instead of just updating the row, I need to delete that row, and re-insert it into the target table. Can anyone show me how to both DELETE and INSERT one after the other whenever I find a MATCH?
Here is an example:
DECLARE #t1 TABLE(id INT IDENTITY, col1 INT)
DECLARE #t2 TABLE(id INT IDENTITY, col1 INT, old_col1 INT)
INSERT INTO #t1(col1) VALUES(5), (10), (15)
INSERT INTO #t2(col1) VALUES(7), (14), (21), (28)
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN INSERT VALUES(t1.col1, NULL)
OUTPUT t1.col1, Deleted.col1 INTO #t2(col1, old_col1);
SELECT * FROM #t2
ORDER BY id
Output:
id col1 old_col1
4 28 NULL
5 5 7
6 10 14
7 15 21
First 3 rows have same ids in both, so match part will delete those(7, 14, 21). 28 will remain. And you insert new values and keep old values in OUTPUT clause.
#Giorgi, that doesn't work completely.
If you #t1 has more rows than #t2, there'll be duplicates.
DECLARE #t1 TABLE(id INT IDENTITY, col1 INT)
DECLARE #t2 TABLE(id INT IDENTITY, col1 INT, old_col1 INT)
INSERT INTO #t1(col1) VALUES(5), (10), (15), (20), (25)
INSERT INTO #t2(col1) VALUES(7), (14), (21), (28)
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN INSERT VALUES(t1.col1, NULL)
OUTPUT t1.col1, Deleted.col1 INTO #t2(col1, old_col1);
SELECT * FROM #t2
ORDER BY id
id col1 old_col1
5 25 NULL
6 25 NULL
7 5 7
8 10 14
9 15 21
10 20 28
The easiest way is run 2 times
DECLARE #t1 TABLE(id INT IDENTITY, col1 INT)
DECLARE #t2 TABLE(id INT IDENTITY, col1 INT, old_col1 INT)
INSERT INTO #t1(col1) VALUES(5), (10), (15), (20), (25)
INSERT INTO #t2(col1) VALUES(7), (14), (21), (28)
-- FIRST RUN;
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN MATCHED THEN DELETE
--SECOND RUN;
MERGE INTO #t2 t2
USING #t1 t1 ON t1.id = t2.id
WHEN NOT MATCHED THEN INSERT VALUES(t1.col1, NULL)

Create a view in sql server using dynamic sql inside

I know this sounds weird, but is it possible to have a view that use dynamic SQL to build it? I know the views are compiled so most probably this is not possible. For sure I could do it using an stored procedure instead but I just want to make sure is not possible.
Here I have an example:
declare #Table1 as table (
Id int,
Name nvarchar(50),
Provider nvarchar(50)
)
insert #Table1 values (1, 'John', 'Provider1')
insert #Table1 values (2, 'Peter', 'Provider1')
insert #Table1 values (3, 'Marcus', 'Provider2')
declare #Table2 as table (
Id int,
Info nvarchar(50),
AnotherInfo nvarchar(50)
)
insert #Table2 values (1, 'Expense', '480140')
insert #Table2 values (1, 'Maintenance', '480130')
insert #Table2 values (2, 'Set Up Cost', '480150')
insert #Table2 values (2, 'Something', '480160')
--No columns from Table2
select a.Id, a.Name, a.Provider from #Table1 a left join #Table2 b on a.Id = b.Id
--With columns from Table2
select a.Id, a.Name, a.Provider, b.Info, b.AnotherInfo from #Table1 a left join #Table2 b on a.Id = b.Id
The first select looks like I have repeated data, which is normal because I did the left join, the problem is that for avoiding that I need to perform a distinct and this is what I don't want to do. My example is short but I have much more columns and table is quite big.

How to autoincrement the id without identity?

I'm trying do to a bulk insert from another table in sql server. My query is currently like that :
INSERT INTO Table1(Id, Value)
SELECT ??, Value
FROM Table2;
Now, my problem is obviously by what I replace ??. Id is an integer column without an identity property. I would like that for each inserted row, Id take the current max(Id) + 1.
Can I do that directly in my insert command?
If you were using a newer version of SQL Server (2008+) you could try ROW_NUMBER():
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + ROW_NUMBER() OVER (ORDER BY Value) ID,
Value
FROM Table2;
SQL Fiddle
Since you are using SQL Server 2000, you could try like bellow:
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.Value <= a.Value),
a.Value
FROM Table2 a
But it will only works if Value in Table2 is unique
SQL Fiddle
If Table2 has a primary key (field PK), then you could use:
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.PK <= a.PK),
a.Value
FROM Table2 a
Here is one wicked way.
We create a temp table with identity to generate new ids. This way we avoid the while loop.
DECLARE #CurrentMaxID INT,
#DynamicQuery NVARCHAR(MAX)
--TODO : Acquired table lock here on table1
SELECT #FirstNextID = ISNULL(MAX(Id), 0)
FROM Table1 --WITH(TABLOCK)
CREATE TABLE #TempTableWithID( Table2Id INT,
Table1FuturId INT IDENTITY(1, 1))
INSERT INTO #TempTableWithID(Table2Id)
SELECT Id --Here we use identity to generate table1 futur id
FROM Table2
INSERT INTO Table1(Id, value)
SELECT Temp.Table1FuturId + #FirstNextID,
Table2.Value
FROM Table2
INNER JOIN #TempTableWithID AS Temp ON Table2.Id = Temp.Table2Id
--TODO : release table lock here on table1
DROP TABLE #TempTableWithID
If I'm understanding you correctly, this should work.
CREATE TABLE #tbl1 (ID int, Value float)
CREATE TABLE #tbl2 (ID int, Value float)
INSERT INTO #tbl2 values (4, 2.0)
INSERT INTO #tbl2 values (8, 3.0)
INSERT INTO #tbl2 values (6, 4.0)
INSERT INTO #tbl1 values (1,1.0)
INSERT INTO #tbl1 values (3,3)
INSERT INTO #tbl1 values (9,3)
/*meat and potatoes start*/
INSERT INTO #tbl1(Id, Value)
SELECT (SELECT MAX(ID) FROM #tbl1) + ROW_NUMBER() OVER (ORDER BY Value) ID, Value
FROM #tbl2;
/*meat and potatoes end*/
Select * From #tbl1
drop table #tbl1
drop table #tbl2
Why not IDENT_CURRENT() ?
SELECT IDENT_CURRENT('yourtablename')
It gives you the next ID reference. But this only works if the ID column has IDENTITY turned on.
OR you can try a SEQUENCE and the NEXT VALUE FOR.
i.e.
CREATE TABLE Test.TestTable
(CounterColumn int PRIMARY KEY,
Name nvarchar(25) NOT NULL) ;
GO
INSERT Test.TestTable (CounterColumn,Name)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Syed') ;
GO
SELECT * FROM Test.TestTable;
GO

T-SQL: Using INSERT INTO & OUTPUT inside CASE clause

I need to assign a new_id to a field inside a CASE clause.
Here is the example:
INSERT INTO [table1]
(field1, field2, field3, field4, field5, field6)
SELECT #someID1, field1_from_table2, #someID2, field2_from_table2,
CASE
WHEN field3_from_table2 IS NULL
THEN INSERT INTO [table3]
OUTPUT inserted.some_new_id
DEFAULT VALUES
ELSE field3_from_table2
END
FROM [table2]
The code fragment
INSERT INTO [table3]
OUTPUT inserted.some_new_id
DEFAULT VALUES
works fine when used outside of the CASE clause.
The main problem is that I have to assign a new_id obtained from table3 when the field3_from_table2 I'm trying to insert into table1 IS NULL.
table3 is just an ID dispatcher.
Any idea or workaround to this problem?
Thanks in advance.
As pointed by marc_s, you can't do that using the a case.
You can see the correct case syntax here.
To solve your problem the best solution is to use a cursor.
You can see an code example for a sp with a cursor here.
Assuming table3 generates sequential integer values, like a IDENTITY property with increments of value 1, you can generate those yourself using ROW_NUMBER(), and then INSERTing the generated values into table3.
But, since you're generating the values yourself, you must lock table3 to prevent changes ocurring to it while the statement runs.
Let's set up a test case.
USE tempdb
GO
IF OBJECT_ID('table1', 'U') IS NOT NULL DROP TABLE table1;
IF OBJECT_ID('table2', 'U') IS NOT NULL DROP TABLE table2;
IF OBJECT_ID('table3', 'U') IS NOT NULL DROP TABLE table3;
CREATE TABLE table1 (
field1 int,
field2 int,
field3 int,
field4 int,
field5 int
);
CREATE TABLE table2 (
field1_from_table2 int,
field2_from_table2 int
);
CREATE TABLE table3 (
field1_from_table3 int IDENTITY(1,1)
);
INSERT INTO table2
VALUES (1000, 2000)
, (1001, NULL);
GO
-- INSERT 20 records to generate some IDENTITY increments.
INSERT INTO table3 DEFAULT VALUES
GO 20
Here's the example code to generate the sequential values.
BEGIN TRANSACTION;
SET IDENTITY_INSERT table3 ON;
GO
DECLARE #someID1 int = 100
, #someID2 int = 200;
DECLARE #output table (field4 int, field5 int);
-- Lock table3 exclusively to prevent INSERTs that spoil IDENTITY values.
SELECT TOP(0) 1 FROM table3 WITH (HOLDLOCK, TABLOCKX);
-- INSERT into table1, generating sequential integers
-- and saving the output in #output.
INSERT INTO table1
OUTPUT inserted.field4
, inserted.field5
INTO #output(field4, field5)
SELECT #someID1
, field1_from_table2
, #someID2
, field2_from_table2
, CASE
WHEN field2_from_table2 IS NOT NULL THEN field2_from_table2
ELSE (ROW_NUMBER() OVER (PARTITION BY field2_from_table2
ORDER BY field2_from_table2))
+ (SELECT MAX(field1_from_table3) FROM table3)
END
FROM table2;
-- INSERT generated integer values.
INSERT INTO table3 (field1_from_table3)
SELECT field5
FROM #output
WHERE field4 IS NULL;
SET IDENTITY_INSERT table3 OFF;
GO
COMMIT;
SELECT * FROM table1;
SELECT * FROM table2;
SELECT * FROM table3;

Resources