Logic Needed for after Parsing Json - sql

DECLARE #json NVARCHAR(MAX)
DECLARE #userid int
SET #userid=3
SET #json =
N'[{"ID":1,"Roles":[1,2]},{"ID":2, "Roles":[1,2,3]},{"ID":3,"Roles":[1,2,3,4]}]'
BEGIN
SELECT ID, R.[Value] AS [Role]
FROM OPENJSON(#json) WITH (
ID INT,
[Roles] NVARCHAR(MAX) AS JSON
)
CROSS APPLY OPENJSON([Roles]) R
end
Above query giving below result.
ID ROLE
1 1
1 2
2 1
2 2
2 3
3 1
3 2
3 3
3 4
CREATE TABLE A(ID INT,ROLE INT)
i want logic to compare above QUERY output with another table(table A) whether above ID is present or not if ID Is present in that table then Role is present or not for corresponding ID,if Role is not present i want to insert Role for the ID.

I believe you can use EXCEPT to filter rows that are not already present in the table:
DECLARE #a TABLE (ID INT, ROLE INT);
INSERT INTO #a VALUES
(1, 2),
(2, 2),
(3, 3);
INSERT INTO #a
SELECT ID, R.[Value] AS [Role]
FROM OPENJSON(#json) WITH (
ID INT,
[Roles] NVARCHAR(MAX) AS JSON
)
CROSS APPLY OPENJSON([Roles]) R
EXCEPT
SELECT ID, ROLE
FROM #a;
Test

Related

Nested while loop in SQL Server is not showing the expected result

I am trying to connect records from two different tables so I can display the data in a tabular format in an SSRS tablix.
The code below does not return the expected results.
As is, for each item in Temp_A the loop updates everything with the last item in Temp_C. Here is the code:
CREATE TABLE #Temp_A
(
[ID] INT,
[Name] VARCHAR(255)
)
INSERT INTO #Temp_A ([ID], [Name])
VALUES (1, 'A'), (2, 'B')
CREATE TABLE #Temp_C
(
[ID] INT,
[Name] VARCHAR(255)
)
INSERT INTO #Temp_C ([ID], [Name])
VALUES (1, 'C'), (2, 'D')
CREATE TABLE #Temp_Main
(
[Temp_A_ID] INT,
[Temp_A_Name] VARCHAR(255),
[Temp_C_ID] INT,
[Temp_C_Name] VARCHAR(255),
)
DECLARE #MIN_AID int = (SELECT MIN(ID) FROM #Temp_A)
DECLARE #MAX_AID int = (SELECT MAX(ID) FROM #Temp_A)
DECLARE #MIN_DID int = (SELECT MIN(ID) FROM #Temp_C)
DECLARE #MAX_DID int = (SELECT MAX(ID) FROM #Temp_C)
WHILE #MIN_AID <= #MAX_AID
BEGIN
WHILE #MIN_DID <= #MAX_DID
BEGIN
INSERT INTO #Temp_Main([Temp_A_ID], [Temp_A_Name])
SELECT ID, [Name]
FROM #Temp_A
WHERE ID = #MIN_AID
UPDATE #Temp_Main
SET [Temp_C_ID] = ID, [Temp_C_Name] = [Name]
FROM #Temp_C
WHERE ID = #MIN_DID
SET #MIN_DID = #MIN_DID + 1
END
SET #MIN_AID = #MIN_AID + 1
SET #MIN_DID = 1
END
SELECT * FROM #Temp_Main
DROP TABLE #Temp_A
DROP TABLE #Temp_C
DROP TABLE #Temp_Main
Incorrect result:
Temp_A_ID | Temp_A_Name | Temp_C_ID | Temp_C_Name
----------+-------------+-----------+---------------
1 A 2 D
1 A 2 D
2 B 2 D
2 B 2 D
Expected results:
Temp_A_ID | Temp_A_Name | Temp_C_ID | Temp_C_Name
----------+-------------+-----------+---------------
1 A 1 C
1 A 2 D
2 B 1 C
2 B 2 D
What am I missing?
You seem to want a cross join:
select a.*, c.*
from #Temp_A a cross join
#Temp_C c
order by a.id, c.id;
Here is a db<>fiddle.
There is no need to write a WHILE loop to do this.
You can use insert to insert this into #TempMain, but I don't se a need to have a temporary table for storing the results of this query.

Assign multiple values to Table variable in SQL

DECLARE #ID INT
SET #ID = (select top 1 USER_REQ_JOB_ID
from T8504_USER_REQ_JOB
where JOB_GRP_ID = 160
order by LST_UPDT_TS desc)
SELECT INPUT_PARM_VAL_TX
from TBL_RPT_JOB_INPUT_PARAM
where USER_REQ_JOB_ID = #ID
This returns these results:
USA
USCC
6
7
2
These five records what I get I want to assign to five different variables to use in stored procedure.
I was trying with table variable like this :
declare #CID table (
Region Char(3)
,Segment Char(3)
,MasterContractId int
,ctcid int
,templateid int)
insert into #CID (Region,Segment,MasterContractId,ctcid,templateid)
But how to insert that 5 rows here?
INSERT INTO #CID
select * from
(
select
'Temp' + convert(char(1), row_number() over (order by (select 0))) as columnName,
INPUT_PARM_VAL_TX as Value
from TBL_RPT_JOB_INPUT_PARAM where USER_REQ_JOB_ID = #ID
) d
pivot
(
max(value)
for columnname in (Temp1, Temp2, Temp3, Temp4, Temp5)
) piv;
See if this helps.
Take a look at this fiddle for an example.
Courtesy:
Add row number to this T-SQL query
Efficiently convert rows to columns in sql server
EDIT: The sql adds an extra column to generate row numbers to use it as an extra column, which is pivoted as column heading.
it's really gross, but one way you could probably do it is this (though you'll need to apply it to your case):
http://sqlfiddle.com/#!6/d41d8/21507
declare #table TABLE (value varchar(50))
INSERT INTO #table
VALUES ('first')
INSERT INTO #table
VALUES ('second')
INSERT INTO #table
VALUES (3)
INSERT INTO #table
VALUES (4)
DECLARE #temp TABLE (id int identity(1,1), value varchar(50))
INSERT INTO #temp
SELECT [value]
FROM #table t
SELECT *
FROM #temp
DECLARE #CID TABLE (Region varchar(50), cont varchar(50), another int, andAnother int)
INSERT INTO #CID
(
Region,
cont,
another,
andAnother
)
VALUES
(
(SELECT value FROM #temp WHERE id = 1), -- Region - varchar
(SELECT value FROM #temp WHERE id = 2), -- cont - varchar
(SELECT value FROM #temp WHERE id = 3), -- another - int
(SELECT value FROM #temp WHERE id = 4) -- andAnother - int
)
SELECT * FROM #cid
note that i assumed you're using mssql, you did not specify

Insert - Select keeping identity mapping

I have 2 tables, and im trying to insert data from one to another and keepeng the mappings between ids.
I found here someone with the same problem, but the solution isnt good for me.
here is the example:
the two tables
CREATE TABLE [source] (i INT identity PRIMARY KEY, some_value VARCHAR(30))
CREATE TABLE [destination] (i INT identity PRIMARY KEY, some_value VARCHAR(30))
CREATE TABLE [mapping] (i_old INT, i_new INT) -- i_old is source.i value, i_new is the inserted destination.i column
some sample data
INSERT INTO [source] (some_value)
SELECT TOP 30 name
FROM sysobjects
INSERT INTO [destination] (some_value)
SELECT TOP 30 name
FROM sysobjects
Here, i want to transfer everything from source into destination, but be able to keep a mapping on the two tables:
I try to use OUTPUT clause, but i cannot refer to columns outside of the ones being inserted:
INSERT INTO [destination] (some_value)
--OUTPUT inserted.i, s.i INTO [mapping] (i_new, i_old) --s.i doesn't work
SELECT some_value
FROM [source] s
Anyone has a solution for this?
Not sure is it write way but it works :D
MERGE [#destination] AS D
USING [#source] AS s
ON s.i <> s.i
WHEN NOT MATCHED BY TARGET
THEN
INSERT (some_value) VALUES (some_value)
OUTPUT inserted.i, s.i INTO [#mapping] (i_new, i_old);
try this sql below if you don't have permission to modify the tables:
The idea is using a temp table to be a bridge between destination table and the mapping table.
SQL Query:
declare #source table (i INT identity PRIMARY KEY, some_value VARCHAR(30))
declare #destination table (i INT identity PRIMARY KEY, some_value VARCHAR(30))
declare #mapping table (i_old INT, i_new INT) -- i_old is source.i value, i_new is the inserted destination.i column
declare #tempSource table
(
id_source INT identity , source_value VARCHAR(30)
,Id_New int,source_new VARCHAR(30)
)
insert into #source
output inserted.i, inserted.some_value into #tempSource(id_source,source_value)
SELECT TOP 10 name
FROM sysobjects
--select * from #tempsource
insert into #destination
OUTPUT inserted.i, inserted.some_value INTO #tempSource (Id_New,source_new)
select source_value from #tempSource
insert into #mapping
select Id_source, Id_New from
(
select a.id_source, a.source_value
from
#tempSource a
where id_source is not null and source_value is not null
) aa
inner join
(
select a.Id_New, a.source_new
from
#tempSource a
where Id_New is not null and source_new is not null
) bb on aa.source_value = bb.source_new
select * from #mapping
The mapping table result:
i_old i_new
----------- -----------
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10

Combine multiple SQL fields into 1 output row

Having a SQL table like
UserID |Attribute | Value
1 |Username | Marius
1 |Password | Fubar
I want to create an output like:
1 | Marius | Fubar
Maybe I'm just too tired to see it, doesn't sound too complicated, but I just can't seem to figure it out. Any help is appreciated.
Why don't you use self joins, ie. :
select u1.userid, u1.value, u2.value
from yourtable u1
inner join yourtable u2 on u2.userid=u1.userid
where u1.attribute='Username' and u2.attribute='Password';
Would something like...
SELECT userID,
GROUP_CONCAT (Value SEPARATOR '|')
FROM my_table
GROUP BY UserID;
be what you are looking for?
If you keep your data that way, you will need to do a crosstab query and pivot your rows into columns. If you are using Microsoft SQL Server, check out "crosstab query" and the pivot operator in Books Online.
Edited:
One column per result:
If you try to combine multiple Attributes with the same UserID it would be something like this:
Short query (one UserID):
declare #concattedtext varchar(1000)
SELECT #concattedtext=coalesce(#concattedtext + '|', '') + Value
FROM #users WHERE UserID=1
SELECT #concattedtext
RESULT with your example data:
1 | Marius | Fubar
Full query (all UserID's)
-- Your source table
CREATE Table #users (UserID int, Attribute varchar(50), Value varchar(50))
-- some entries
INSERT into #users (UserID, Attribute, Value)
VALUES (1, 'Test1', 'attr1')
INSERT into #users (UserID, Attribute, Value)
VALUES (1, 'Test2', 'attr2')
INSERT into #users (UserID, Attribute, Value)
VALUES (1, 'Test3', 'attr3')
INSERT into #users (UserID, Attribute, Value)
VALUES (2, 'Test4', 'attr4')
-- ids table variable (for distinct UserID's)
DECLARE #ids TABLE
(
rownum int IDENTITY (1, 1) Primary key NOT NULL,
UserID int
)
-- Output table variable
DECLARE #out TABLE
(
rownum int IDENTITY (1, 1) Primary key NOT NULL,
UserID int,
ConcatText varchar(1000)
)
-- get distinct id's
INSERT INTO #ids(UserID)
SELECT DISTINCT(UserID) FROM #users
-- Foreach vars
declare #RowCnt int
declare #MaxRows int
select #RowCnt = 1
select #MaxRows=count(*) from #ids
-- UserID
declare #id int
declare #concattedtext varchar(1000)
-- process each id
while #RowCnt <= #MaxRows
begin
SET #id = 0
SELECT #id=UserID
FROM #ids WHERE rownum=#RowCnt
SET #concattedtext = CONVERT(nvarchar(50), #id)
FROM #ids WHERE rownum=#RowCnt
SELECT #concattedtext=coalesce(#concattedtext + '|', '') + Value
FROM #users WHERE UserID=#id
INSERT INTO #out(UserID, ConcatText)
VALUES (#id, #concattedtext)
-- next UserID
Select #RowCnt = #RowCnt + 1
end
SELECT * FROM #out
DROP TABLE #users
Result:
rownum|UserID|ConcatTex
1 | 1 |1|attr1|attr2|attr3
2 | 2 |2|attr4
DROP TABLE #users
You might need a sort field to get your parameters in your requested order.
Multiple columns
Your data needs to have an equal count of attributes if you like to get a table with multiple columns. And you still need to take care about the ordering.
In this case a hardcoded query with a group by would be your choice.

Delete duplicated rows and Update references

How do I Delete duplicated rows in one Table and update References in another table to the remaining row? The duplication only occurs in the name. The Id Columns are Identity columns.
Example:
Assume we have two tables Doubles and Data.
Doubles table (
Id int,
Name varchar(50)
)
Data Table (
Id int,
DoublesId int
)
Now I Have Two entries in the Doubls table:
Id Name
1 Foo
2 Foo
And two entries in the Data Table:
ID DoublesId
1 1
2 2
At the end there should be only one entry in the Doubles Table:
Id Name
1 Foo
And two entries in the Data Table:
Id DoublesId
1 1
2 1
In the doubles Table there can be any number of duplicated rows per name (up to 30) and also regular 'single' rows.
I've not run this, but hopefully it should be correct, and close enough to the final soln to get you there. Let me know any mistakes if you like and I'll update the answer.
--updates the data table to the min ids for each name
update Data
set id = final_id
from
Data
join
Doubles
on Doubles.id = Data.id
join
(
select
name
min(id) as final_id
from Doubles
group by name
) min_ids
on min_ids.name = Doubles.name
--deletes redundant ids from the Doubles table
delete
from Doubles
where id not in
(
select
min(id) as final_id
from Doubles
group by name
)
Note: I have taken the liberty to rename your Id's to DoubleID and DataID respectively. I find that eassier to work with.
DECLARE #Doubles TABLE (DoubleID INT, Name VARCHAR(50))
DECLARE #Data TABLE (DataID INT, DoubleID INT)
INSERT INTO #Doubles VALUES (1, 'Foo')
INSERT INTO #Doubles VALUES (2, 'Foo')
INSERT INTO #Doubles VALUES (3, 'Bar')
INSERT INTO #Doubles VALUES (4, 'Bar')
INSERT INTO #Data VALUES (1, 1)
INSERT INTO #Data VALUES (1, 2)
INSERT INTO #Data VALUES (1, 3)
INSERT INTO #Data VALUES (1, 4)
SELECT * FROM #Doubles
SELECT * FROM #Data
UPDATE #Data
SET DoubleID = MinDoubleID
FROM #Data dt
INNER JOIN #Doubles db ON db.DoubleID = dt.DoubleID
INNER JOIN (
SELECT db.Name, MinDoubleID = MIN(db.DoubleID)
FROM #Doubles db
GROUP BY db.Name
) dbmin ON dbmin.Name = db.Name
/* Kudos to quassnoi */
;WITH q AS (
SELECT Name, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Name) AS rn
FROM #Doubles
)
DELETE
FROM q
WHERE rn > 1
SELECT * FROM #Doubles
SELECT * FROM #Data
Take a look at this one, i have tried this, working fine
--create table Doubles ( Id int, Name varchar(50))
--create table Data( Id int, DoublesId int)
--select * from doubles
--select * from data
Declare #NonDuplicateID int
Declare #NonDuplicateName varchar(max)
DECLARE #sqlQuery nvarchar(max)
DECLARE DeleteDuplicate CURSOR FOR
SELECT Max(id),name AS SingleID FROM Doubles
GROUP BY [NAME]
OPEN DeleteDuplicate
FETCH NEXT FROM DeleteDuplicate INTO #NonDuplicateID, #NonDuplicateName
--Fetch next record
WHILE ##FETCH_STATUS = 0
BEGIN
--select b.ID , b.DoublesID, a.[name],a.id asdasd
--from doubles a inner join data b
--on
--a.ID=b.DoublesID
--where b.DoublesID<>#NonDuplicateID
--and a.[name]=#NonDuplicateName
print '---------------------------------------------';
select
#sqlQuery =
'update b
set b.DoublesID=' + cast(#NonDuplicateID as varchar(50)) + '
from
doubles a
inner join
data b
on
a.ID=b.DoublesID
where b.DoublesID<>' + cast(#NonDuplicateID as varchar(50)) +
' and a.[name]=''' + cast(#NonDuplicateName as varchar(max)) +'''';
print #sqlQuery
exec sp_executeSQL #sqlQuery
print '---------------------------------------------';
-- now move the cursor
FETCH NEXT FROM DeleteDuplicate INTO #NonDuplicateID ,#NonDuplicateName
END
CLOSE DeleteDuplicate --Close cursor
DEALLOCATE DeleteDuplicate --Deallocate cursor
---- Delete duplicate rows from original table
DELETE
FROM doubles
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM doubles
GROUP BY [NAME]
)
Please try and let me know if this helped you
Thanks
~ Aamod
If you are using MYSQL following worked for me. I did it for 2 steps
Step 1 -> Update all Data rows to one Double table reference (with lowest id)
Step 2 -> Delete all duplicates with keeping lowest id
Step 1 ->
update Data
join
Doubles
on Data.DoublesId = Doubles.id
join
(
select name, min(id) as final_id
from Doubles
group by name
) min_ids
on min_ids.name = Doubles.name
set DoublesId = min_ids.final_id;
Step 2 ->
DELETE c1 FROM Doubles c1
INNER JOIN Doubles c2
WHERE
c1.id > c2.id AND
c1.name = c2.name;