TSQL IF/ELSE or CASE (UPSERT) - sql-server

Not sure if IF/ELSE is the right way to go for the following. It always returns ELSE so it seems its not working correctly.
IF ((SELECT COUNT(CAST(StudentuserID AS int)) FROM StudentAttendance WHERE StudentUserID=1)>0)
PRINT 'Yes'
ELSE
PRINT 'No'
This test should result in yes as the data is 8>0
I will be replacing PRINT with an UPDATE ELSE INSERT statement.
IF ((SELECT COUNT(CAST(StudentuserID AS int)) FROM StudentAttendance WHERE StudentUserID=1)>0)
UPDATE StudentAttendance
SET
CID = CAST('[querystring:CID]' AS int),
CalendarEventID = CAST('[querystring:CEID]' AS int),
StudentUserID = CAST('[StudentUserID]' AS int),
Attendance = '[Attendance]'
ELSE
INSERT INTO StudentAttendance
(CID,CalendarEventID,StudentUserID,Attendance)
VALUES
(CAST('[querystring:CID]' AS int), CAST('[querystring:CEID]' AS int), CAST('[StudentsUserID]' AS int),'[Attendance]')

It looks like your IF/ELSE would work fine (it looks like you're doing this for one record in a stored procedure or something?). If it's currently returning 'No' and you don't think it should be, I'd perhaps do a more basic check on your table, e.g.:
SELECT *
FROM StudentAttendance
WHERE StudentUserID = 1
You can also use a MERGE statement for this, and you can use multiple source tables by joining them within the USING part. Here is a basic example of that:
DECLARE #A table (Aid int, value int)
DECLARE #B table (Aid int, Cid int)
DECLARE #C table (Cid int, value int)
INSERT INTO #A VALUES (1, 1)
INSERT INTO #B VALUES (1, 2)
INSERT INTO #B VALUES (2, 3)
INSERT INTO #C VALUES (2, 4)
INSERT INTO #C VALUES (3, 6)
;
SELECT *
FROM #A
;
MERGE INTO #A tgt
USING (SELECT B.Aid, B.Cid, C.value FROM #B B JOIN #C C ON B.Cid = C.Cid) src
ON tgt.Aid = src.Aid
WHEN MATCHED THEN UPDATE
SET tgt.value = src.value
WHEN NOT MATCHED THEN
INSERT
(
Aid
, value
)
VALUES
(
src.Aid
, src.value
)
;
SELECT *
FROM #A
;

Related

Insert Into Table with String Insert Or Table Type

I have a table called #Tbl1, Each GROUP is 1 row and I have to extract the number of rows for each to #Tbl_Insert type.
Declare #Tbl1 Table (TableName NVARCHAR(250),ColumnName NVARCHAR(250),DataType NVARCHAR(250),DataValue NVARCHAR(250),InGroup NVARCHAR(250))
Declare #Tbl_Insert Table (ID INT, Name NVARCHAR(250), Age INT)
-- Sample Data
Insert Into #Tbl1 values ('#Tbl_Insert','ID','INT','1','Group1'),('#Tbl_Insert','Name','NVARCHAR(250)','John.Adam','Group1'),('#Tbl_Insert','Age','INT','10','Group1')
Insert Into #Tbl1 values ('#Tbl_Insert','ID','INT','2','Group2'),('#Tbl_Insert','Name','NVARCHAR(250)','Andy.Law','Group2'),('#Tbl_Insert','Age','INT','18','Group2')
I can convert #tbl1 to row by row into #Table_TEMP
Declare #Table_TEMP (Data nvarchar(max))
Insert Into #Table_TEMP
SELECT LEFT([DataValues] , LEN([DataValues] )-1)
FROM #Tbl1 AS extern
CROSS APPLY
(
SELECT Concat('''', Replace( ISNULL([DataValue],''), '''','' ) + ''',')
FROM #Tbl1 AS intern
WHERE extern.InGroup = intern.InGroup
Order By InGroup, ColumnName
FOR XML PATH('')
) pre_trimmed ( [DataValues])
GROUP BY InGroup, [DataValues]
I have to extract the number of rows in #Tbl1 ( Or #Table_TEMP) to #Tbl_Insert.
I don't want to use cursor to loop Insert row by row in #Table_TEMP, because, when you met with big data (example > 10000 rows). It's run to slow.
Please help.
I found sample in stackorverflow
Declare #tbl_Temp Table (Data NVARCHAR(MAX))
Declare #tbl2 Table (A NVARCHAR(MAX),B NVARCHAR(MAX),C NVARCHAR(MAX))
Insert Into #tbl_Temp values ('a1*b1*c1')
INSERT INTO #tbl2 (A,B,C)
SELECT PARSENAME(REPLACE(Data,'*','.'),3)
,PARSENAME(REPLACE(Data,'*','.'),2)
,PARSENAME(REPLACE(Data,'*','.'),1)
FROM #tbl_Temp
select * from #tbl2
It's nearly the same, but,
My data have "DOT", can not use PARSENAME
I must know numbers of DOT to Build Dynamics SQL??
PARSENAME only support 3 "DOT", It's null when More Dot.
EXAMPLE:
Declare #ObjectName nVarChar(1000)
Set #ObjectName = 'HeadOfficeSQL1.Northwind.dbo.Authors'
SELECT
PARSENAME(#ObjectName, 5) as Server4,
PARSENAME(#ObjectName, 4) as Server,
PARSENAME(#ObjectName, 3) as DB,
PARSENAME(#ObjectName, 2) as Owner,
PARSENAME(#ObjectName, 1) as Object
If, i understand correctly you will need to use apply in order to fetch the records & insert the data into other table
insert into #Tbl_Insert (ID, Name, Age)
select max(a.id) [id], max(a.Name) [Name], max(a.Age) [Age] from #Tbl1 t
cross apply
(values
(case when t.ColumnName = 'ID' then t.DataValue end,
case when t.ColumnName = 'Name' then t.DataValue end,
case when t.ColumnName = 'Age' then t.DataValue end, t.InGroup)
) as a(id, Name, Age, [Group])
group by a.[Group]
select * from #Tbl_Insert
I do both #Tbl_Insert & create 1 store to do like PARSENAME. It's improved performance.
create function dbo.fnGetCsvPart(#csv varchar(8000),#index tinyint, #last bit = 0)
returns varchar(4000)
as
/* function to retrieve 0 based "column" from csv string */
begin
declare #i int; set #i = 0
while 1 = 1
begin
if #index = 0
begin
if #last = 1 or charindex(',',#csv,#i+1) = 0
return substring(#csv,#i+1,len(#csv)-#i+1)
else
return substring(#csv,#i+1,charindex(',',#csv,#i+1)-#i-1)
end
select #index = #index-1, #i = charindex(',',#csv,#i+1)
if #i = 0 break
end
return null
end
GO

MERGE between 2 tables, one table with 10million rows

TableA
match / Keyword
0 Stackoverflow
1 Youtube
1 Google
0 Yandex
1 Twitter
0 Facebook
0 Teacher
Totally 10million rows in TableA
There is Clustered index at Keyword column
TableB
match / word
1 You
1 Go
1 Twit
0 Home
0 Car
0 Pencil
0 Money
0 Weather
0 Her
Totally 500 rows in TableB
There is Clustered index at word column
My Question
i want to make a sql query to match every word from TableB if matches in TableA keywords. And update the TableB.match with 1
(TableA.keyword like '+TableB.word+'%') (will be matched)
NOT the middle of the keyword matches; (TableA.keyword like '%'+TableB.word+'%')
Forexample Her -> in Teacher (wont be matched)
I Tried to use MERGE
First Try;
i tried to match keywords with words and update TableB
i get error, because there is multiple matches in TableA and MERGE do not allow updating multiple times a row in Target table (TableB)
MERGE INTO [TableB] As XB
USING (Select keyword FROM [TableA]) As XA
ON XB.word LIKE ''+XA.keyword+'%'
WHEN MATCHED THEN UPDATE SET XB.match=1;
Second Try;
i tried to match words with keywords and update TableA
i get what i want, The problem is, it takes 1 hour to execute the query for 500words in 10million keywords.
MERGE INTO [TableA] As XA
USING (Select word FROM [TableB]) As XB
ON XB.word LIKE ''+XA.keyword+'%'
WHEN MATCHED THEN UPDATE SET XA.match=1;
Is there an option to fasten these lookups in SecondTry?
An update statement will suffice for what you're trying to do. Note that this will probably not perform very well as SQL isn't great at comparing strings.
declare #a table (match int, keyword varchar(50))
declare #b table (match int, keyword varchar(50))
insert into #a values (0, 'Stackoverflow')
insert into #a values (0, 'Youtube')
insert into #a values (0, 'Google')
insert into #a values (0, 'Yandex')
insert into #a values (0, 'Twitter')
insert into #a values (0, 'Facebook')
insert into #a values (0, 'Teacher')
insert into #b values (0, 'You')
insert into #b values (0, 'Go')
insert into #b values (0, 'Twit')
insert into #b values (0, 'Home')
insert into #b values (0, 'Car')
insert into #b values (0, 'Pencil')
insert into #b values (0, 'Money')
insert into #b values (0, 'Weather')
insert into #b values (0, 'Her')
--commented out because user didn't want this, but it matches the provided data
--update #a
--set match = 1
--where keyword in
--(
-- select
-- distinct a.keyword
-- from #a a
-- cross apply #b b
-- where a.keyword like b.keyword + '%'
--)
update #b
set match = 1
where keyword in
(
select
distinct b.keyword
from #a a
cross apply #b b
where a.keyword like b.keyword + '%'
)
select *
from #a
select *
from #b
--EDIT BY Sean--
Here is how you could do this as a correlated subquery so you can use EXISTS.
update b
set match = 1
from #b b
where exists
(
select b.keyword
from #a a
where a.keyword like b.keyword + '%'
)

SQLServer : Grouping and Replacing a COLUMN value with the DATA from other table, without UDF

I would like to replace the numbers in #CommentsTable column "Comments" with the equivalent text from #ModTable table, without using UDF in a single SELECT. May with a CTE. Tried STUFF with REPLACE, but no luck.
Any suggestions would be a great help!
Sample:
DECLARE #ModTable TABLE
(
ID INT,
ModName VARCHAR(10),
ModPos VARCHAR(10)
)
DECLARE #CommentsTable TABLE
(
ID INT,
Comments VARCHAR(100)
)
INSERT INTO #CommentsTable
VALUES (1, 'MyFirst 5 Comments with 6'),
(2, 'MySecond comments'),
(3, 'MyThird comments 5')
INSERT INTO #ModTABLE
VALUES (1, '[FIVE]', '5'),
(1, '[SIX]', '6'),
(1, '[ONE]', '1'),
(1, '[TWO]', '2')
SELECT T1.ID, <<REPLACED COMMENTS>>
FROM #CommentsTable T1
GROUP BY T1.ID, T1.Comments
**Expected Result:**
ID Comments
1 MyFirst [FIVE] Comments with [SIX]
2 MySecond comments
3 MyThird comments [FIVE]
Create a cursor, span over the #ModTable and do each replacement a time
DECLARE replcursor FOR SELECT ModPos, ModName FROM #ModTable;
OPEN replcursor;
DECLARE modpos varchar(100) DEFAULT "";
DECLARE modname varchar(100) DEFAULT "";
get_loop: LOOP
FETCH replcursor INTO #modpos, #modname
SELECT T1.ID, REPLACE(T1.Comments, #modpos, #modname)
FROM #CommentsTable T1
GROUP BY T1.ID, T1.Comments
END LOOP get_loop;
Of course, you can store the results in a temp table and get the results altogether in the end of loop.
You can use a while loop to iterate over the records and the mods. I slightly modified your #ModTable to have unique values for ID. If this is not your data structure, then you can use a window function like ROW_NUMBER() to get a unique value over which you can iterate.
Revised script example:
DECLARE #ModTable TABLE
(
ID INT,
ModName VARCHAR(10),
ModPos VARCHAR(10)
)
DECLARE #CommentsTable TABLE
(
ID INT,
Comments VARCHAR(100)
)
INSERT INTO #CommentsTable
VALUES (1, 'MyFirst 5 Comments with 6'),
(2, 'MySecond comments'),
(3, 'MyThird comments 5')
INSERT INTO #ModTABLE
VALUES (1, '[FIVE]', '5'),
(2, '[SIX]', '6'),
(3, '[ONE]', '1'),
(4, '[TWO]', '2')
declare #revisedTable table (id int, comments varchar(100))
declare #modcount int = (select count(*) from #ModTable)
declare #commentcount int = (select count(*) from #CommentsTable)
declare #currentcomment varchar(100) = ''
while #commentcount > 0
begin
set #modcount = (select count(*) from #ModTable)
set #currentcomment = (select Comments from #CommentsTable where ID = #commentcount)
while #modcount > 0
begin
set #currentcomment = REPLACE( #currentcomment,
(SELECT TOP 1 ModPos FROM #ModTable WHERE ID = #modcount),
(SELECT TOP 1 ModName FROM #ModTable WHERE ID = #modcount))
set #modcount = #modcount - 1
end
INSERT INTO #revisedTable (id, comments)
SELECT #commentcount, #currentcomment
set #commentcount = #commentcount - 1
end
SELECT *
FROM #revisedTable
order by id
I think the will work even though I generally avoid recursive queries. It assumes that you have consecutive ids though:
with Comments as
(
select ID, Comments, 0 as ConnectID
from #CommentsTable
union all
select ID, replace(c.Comments, m.ModPos, m.ModName), m.ConnectID
from Comments c inner join #ModTable m on m.ConnectID = c.ConnectID + 1
)
select * from Comments
where ConnectID = (select max(ID) from #ModTable)
=> CLR Function()
As I have lot of records in "CommentsTable" and the "ModTable" would have multiple ModName for each comments, finally decided to go with CLR Function. Thanks all of you for the suggestions and pointers.

SQL Server select (top) two rows into two temp variables

I have a query which results in two or more rows (just one column) and I want to catch the first row value into first temp variable and second row value into second temp variable without using multiple times the select top 1 and select top 1 order by desc
Something like this;
Select row1 value into #tempvariable1, row2 value into #tempvariable2 from blah blah
You need somehow to identify the row (I am using a row ID in the example below, ordering by value - you can order by id or something else):
DECLARE #DataSource TABLE
(
[value] VARCHAR(12)
);
INSERT INTO #DataSource
VALUES ('value 1')
,('value 2')
,('value 3');
DECLARE #tempVariable1 VARCHAR(12)
,#tempVariable2 VARCHAR(12);
WITH DataSource ([value], [rowID]) AS
(
SELECT [value]
,ROW_NUMBER() OVER (ORDER BY [value])
FROM #DataSource
)
SELECT #tempVariable1 = IIF([rowID] = 1, [value], #tempVariable1)
,#tempVariable2 = IIF([rowID] = 2, [value], #tempVariable2)
FROM DataSource;
SELECT #tempVariable1
,#tempVariable2;
You can use a CTE where you will get the X values you need and then select from it:
declare #data table(id int);
insert into #data(id) values(8), (6), (4), (3);
with vals(id, n) as (
Select top(2) id, ROW_NUMBER() over(order by id)
From #data
)
Select #A = (Select id From vals Where n = 1)
, #B = (Select id From vals Where n = 2)
You could also use PIVOT:
Select #A = [1], #B = [2]
From (
Select id, ROW_NUMBER() over(order by id)
From #data
) v(id, n)
PIVOT (
max(id) FOR n in ([1], [2])
) as piv
You have two options
Let's say we test case is build as below
create table dbo.Test
(
value varchar(100) not null
)
GO
insert into dbo.Test
values
('A'),('B'),('NO THIS ONE'),('NO THIS ONE'),('NO THIS ONE')
GO
Now let's say you fetch your data as below
select t.value
from dbo.Test t
where t.value != 'NO THIS ONE'
GO
The first and easier option is to save the data in a temp table
declare #results as Table (value varchar(100))
insert into #results
select t.value
from dbo.Test t
where t.value != 'NO THIS ONE'
you still use TOP 1 BUT not in the entire data, only in the results.
Use TOP 1 to find the first result and a second TOP 1 where value is different from the first.
declare #A varchar(100), #B varchar(100)
set #A = (select top 1 r.value from #results r)
set #B = (select top 1 r.value from #results r where r.value != #A)
select #A, #B
GO
This approach have the advantage of performance.
Of course that don't work great if both values are equal. You can fix it by using a top 1 and ordering in the inverse order.
There's a better alternative using rownumber.
It works because if you set a variable when returning multiple rows the varible sticks with the last one (in fact it's reseted for each row iteration).
The case statement makes sure the variable #A is seted only on the first row iteration.
declare #A varchar(100), #B varchar(100)
/* This way #B receives the last value and #A the first */
select #B = t.value,
#A = (case when ROW_NUMBER() OVER(order by t.Value) = 1
then t.Value else #A
end)
from dbo.Test t
where t.value != 'NO THIS ONE'
select #A, #B

SQL Query Uniqueness with subjoin

Help! Here is a very simple a,b,c sample of what I need to accomplish. I have been pulling my hair out. I've written this before but can't get my head around it now! So here it is, with actual and expected results demonstrated below:
set nocount on
declare #a table (id int, a varchar(10))
declare #b table (ref int, b varchar(10), c varchar(20))
insert into #a select 1, 'bingo'
insert into #a select 2, 'bongo'
insert into #b select 1, 'T5', 'asdfwef'
insert into #b select 1, 'T8', 'asfqwez'
insert into #b select 1, 'T6', 'qweoae'
insert into #b select 1, 'T8', 'qzoeqe'
insert into #b select 1, 'T9', 'oqeizef'
insert into #b select 2, 'T3', 'awega'
insert into #b select 2, 'T6', 'fhaeaw'
insert into #b select 2, 'T3', 'fqsegw'
select * from #a a join #b b on a.id = b.ref
-- Expected (Uniqueness is: a’s id to b’s ref and the first b value ingoring b’s c value)
----1,bingo,1,T5,asdfwef
----1,bingo,1,T8,asfqwez
----1,bingo,1,T6,qweoae
----1,bingo,1,T9,oqeizef
----2,bongo,2,T3,awega
----2,bongo,2,T6,fhaeaw
-- Actual
----1,bingo,1,T5,asdfwef
----1,bingo,1,T8,asfqwez
----1,bingo,1,T6,qweoae
----1,bingo,1,T8,qzoeqe
----1,bingo,1,T9,oqeizef
----2,bongo,2,T3,awega
----2,bongo,2,T6,fhaeaw
----2,bongo,2,T3,fqsegw
Your query is returning the correct results. All the matching values from #b.
If you want the first b value, you need to do two things. First, you need to include an ordering column in b so you know what "first" is. Remember, SQL tables are unordered. This is easy:
declare #b table (id int identity(1,1) not null, ref int, b varchar(10), c varchar(20));
You then have to change the inserts to insert all but the id:
insert into #b(ref, b, c) select 1, 'T5', 'asdfwef';
Now you are ready for the actual query:
select *
from #a a join
(select b.*, row_number() over (partition by b.ref, b.b order by b.id) as seqnum
from #b b
) b
on a.id = b.ref and b.seqnum = 1

Resources