TSQL-Select - Turn multiple Rows into single Row - sql-server

I have to deal with the following table structure where columns are stored as rows:
CREATE TABLE Test
(
ID varchar(10),
RowNumber int,
Column1 varchar(200), -- sheet name
Column2 varchar(200), -- column name
Column3 varchar(200) -- column cell value
);
INSERT INTO Test
(ID, RowNumber,Column1,Column2, Column3)
VALUES
('ID0001', 1, 'sheet abc','InternalNumber','2016_2923_2321'),
('ID0001', 2, 'sheet abc','Customer','ACME'),
('ID0001', 3, 'sheet abc','Project','ABC_Project'),
('ID0001', 4, 'another sheet name','Subject','New Model'),
('ID0001', 5, 'another sheet name','SOP','2016'),
('ID0001', 6, 'another sheet name','EOP','2022')
I managed to turn the rows into proper columns (see fiddle at the bottom) but instead of getting multiple rows I want to put the data into a single row as each value in Column2 is a unique name.
In below picture at the top is the input table, the second one is what I have and the third is my desired output I cannot get to work:
See also my fiddle
I hope you can help me solve this issue.
Thank you in advance.

The basic way is to use PIVOT:
SELECT *
FROM (
SELECT ID,
Column2,
Column3
FROM #Test
) as t
PIVOT (
MAX(Column3) FOR Column2 IN ([InternalNumber],[Customer],[Project],[Subject],[SOP],[EOP])
) as p
Output:
ID InternalNumber Customer Project Subject SOP EOP
ID0001 2016_2923_2321 ACME ABC_Project New Model 2016 2022
If there are much more values in Column2 better use dynamic SQL.

Related

How to find specific characters in a string and replace them with values fetched from a table in SQL Server

I have text stored in the table "StructureStrings"
Create Table StructureStrings(Id INT Primary Key,String nvarchar(4000))
Sample Data:
Id String
1 Select * from Employee where Id BETWEEN ### and ### and Customer Id> ###
2 Select * from Customer where Id BETWEEN ### and ###
3 Select * from Department where Id=###
and I want to replace the "###" word with a values fetched from another table
named "StructureValues"
Create Table StructureValues (Id INT Primary Key,Value nvarcrhar(255))
Id Value
1 33
2 20
3 44
I want to replace the "###" token present in the strings like
Select * from Employee where Id BETWEEN 33 and 20 and Customer Id> 44
Select * from Customer where Id BETWEEN 33 and 20
Select * from Department where Id=33
PS: 1. Here an assumption is that the values will be replaced with the tokens in the same order i.e first occurence of "###" will be replaced by first value of
"StructureValues.Value" column and so on.
Posting this as a new answer, rather than editting my previous.
This uses Jeff Moden's DelimitedSplit8K; it does not use the built in splitter available in SQL Server 2016 onwards, as it does not provide an item number (thus no join criteria).
You'll need to firstly put the function on your server, then you'll be able to use this. DO NOT expect it to perform well. There's a lot of REPLACE in this, which will hinder performance.
SELECT (SELECT REPLACE(DS.Item, '###', CONVERT(nvarchar(100), SV.[Value]))
FROM StructureStrings sq
CROSS APPLY DelimitedSplit8K (REPLACE(sq.String,'###','###|'), '|') DS --NOTE this uses a varchar, not an nvarchar, you may need to change this if you really have Unicode characters
JOIN StructureValues SV ON DS.ItemNumber = SV.Id
WHERE SS.Id = sq.id
FOR XML PATH ('')) AS NewString
FROM StructureStrings SS;
If you have any question, please place the comments on this answer; do not put them under the question which has already become quite a long discussion.
Maybe this is what you are looking for.
DECLARE #Employee TABLE (Id int)
DECLARE #StructureValues TABLE (Id int, Value int)
INSERT INTO #Employee
VALUES (1), (2), (3), (10), (15), (20), (21)
INSERT INTO #StructureValues
VALUES (1, 10), (2, 20)
SELECT *
FROM #Employee
WHERE Id BETWEEN (SELECT MIN(Value) FROM #StructureValues) AND (SELECT MAX(Value) FROM #StructureValues)
Very different take here:
CREATE TABLE StructureStrings(Id int PRIMARY KEY,String nvarchar(4000));
INSERT INTO StructureStrings
VALUES (1,'SELECT * FROM Employee WHERE Id BETWEEN ### AND ###'),
(2,'SELECT * FROM Customer WHERE Id BETWEEN ### AND ###');
CREATE TABLE StructureValues (Id int, [Value] int);
INSERT INTO StructureValues
VALUES (1,10),
(2,20);
GO
DECLARE #SQL nvarchar(4000);
--I'm asuming that as you gave one output you are supplying an ID or something?
DECLARE #Id int = 1;
WITH CTE AS(
SELECT SS.Id,
SS.String,
SV.[Value],
LEAD([Value]) OVER (ORDER BY SV.Id) AS NextValue,
STUFF(SS.String,PATINDEX('%###%',SS.String),3,CONVERT(varchar(10),[Value])) AS ReplacedString
FROM StructureStrings SS
JOIN StructureValues SV ON SS.Id = SV.Id)
SELECT #SQL = STUFF(ReplacedString,PATINDEX('%###%',ReplacedString),3,CONVERT(varchar(10),NextValue))
FROM CTE
WHERE Id = #Id;
PRINT #SQL;
--EXEC (#SQL); --yes, I should really be using sp_executesql
GO
DROP TABLE StructureValues;
DROP TABLE StructureStrings;
Edit: Note that Id 2 will return NULL, as there isn't a value to LEAD to. If this needs to change, we'll need more logic on what the value should be if there is not value to LEAD to.
Edit 2: This was based on the OP's original post, not what he puts it as later. As it currently stands, it's impossible.

How to iterate through a given xml input and insert the data in a TempTable in SQL Server?

Hi I have the following xml input in a SP.
DECLARE #XmlVariable XML = '<portal><patientid>67518</patientid>
<forms>
<form id="31" type="C"/>
<form id="44" type="D"/>
</forms>
</portal>'
I have the following inmemory table:
DECLARE #TColumns table (
FormId int,
FormType varchar(1),
PatientId int
)
Now, my intention is to:
1.Iterate the xml and insert the values into the #TColumns table.
2.Read the #TColumns table row by row and based on the 3 column values update some existing table;something like
update myexistingtable set status=4 where Formid=31 && Formtype='C' and PatientId=67518.
For item number 1 above, this is what I have done till now, but there is some syntax error:
INSERT INTO #TColumns(FormId,FormType,PatientId)
SELECT
XTbl.Cats.value('.', 'int'),
XTbl.Cats.value('.', 'varchar(1)'),
XTbl.Cats.value('.', 'int')
FROM
#XmlVariable.nodes('/portal/forms/form/#id') AS XTbl(Cats),
#XmlVariable.nodes('/portal/forms/form/#type') AS XTbl(Cats),
#XmlVariable.nodes('/portal/forms/form/#patientid') AS XTbl(Cats)
The error I am getting is:The correlation name 'XTbl' is specified multiple times in a FROM clause.
Need help on this and also on the item number 2 above.
Thanks in advance.
Maybe you want something like this
SELECT
Tbl1.Form.value('#id', 'int'),
Tbl1.Form.value('#type', 'varchar(1)'),
Tbl2.Portal.value('patientid', 'int')
FROM
#XmlVariable.nodes('//form') Tbl1(Form),
#XmlVariable.nodes('//portal') Tbl2(Portal)
This is what helped.Yes its is based on Hogan's last suggestion.Thank you!
INSERT INTO #TColumns(FormId,FormType,PatientId)
SELECT
Tbl1.Form.value('#id', 'int'),
Tbl1.Form.value('#type', 'varchar(1)'),
Tbl2.Portal.value('.', 'int')
FROM
#XmlVariable.nodes('//form') Tbl1(Form),
#XmlVariable.nodes('//patientid') Tbl2(Portal)

Speed up to offset in SQL Server 2014

I have a table with about 70000000 rows of phone numbers. I use OFFSET to read those 50 by 50 numbers.
But it takes a long time (about 1 min).
However, that full-text index used for search and does not impact for offset.
How I can speed up my query?
SELECT *
FROM tblPhoneNumber
WHERE CountryID = #CountryID
ORDER BY ID
OFFSET ((#NumberCount - 1) * #PackageSize) ROWS
FETCH NEXT #PackageSize ROWS ONLY
Throw a sequence on that table, index it and fetch ranges by sequence. You could alternatively just use the ID column.
select *
FROM tblPhoneNumber
WHERE
CountryID = #CountryID
and Sequence between #NumberCount and (#NumberCount + #PackageSize)
If you're inserting/deleting frequently, this can leave gaps, so depending on the code that utilizes these batches of numbers, this might be a problem, but in general a few gaps here and there may not be a problem for you.
Try using CROSS APPLY instead of OFFSET FETCH and do it all in one go. I grab TOP 2 to show you that you can grab any number of rows.
IF OBJECT_ID('tempdb..#tblPhoneNumber') IS NOT NULL
DROP TABLE #tblPhoneNumber;
IF OBJECT_ID('tempdb..#Country') IS NOT NULL
DROP TABLE #Country;
CREATE TABLE #tblPhoneNumber (ID INT, Country VARCHAR(100), PhoneNumber INT);
CREATE TABLE #Country (Country VARCHAR(100));
INSERT INTO #Country
VALUES ('USA'),('UK');
INSERT INTO #tblPhoneNumber
VALUES (1,'USA',11111),
(2,'USA',22222),
(3,'USA',33333),
(4,'UK',44444),
(5,'UK',55555),
(6,'UK',66666);
SELECT *
FROM #Country
CROSS APPLY(
SELECT TOP (2) ID,Country,PhoneNumber --Just change to TOP(50) for your code
FROM #tblPhoneNumber
WHERE #Country.Country = #tblPhoneNumber.Country
) CA

I am having trouble with SQL Server Express's identity feature

I'm working with SQL Server Express, I created this table
CREATE TABLE inventory
(
id INT NOT NULL IDENTITY(1,1),
description nvarchar(50),
quantity int,
price money
)
when I insert this statement:
INSERT INTO inventory VALUES('water', 20, 1.50)
I get this error:
The number of columns in the query and the table must match. [ Number
of columns in query = 3, Number of columns in table = 4 ]
and when I put this statement:
INSERT INTO inventory VALUES(1, 'water', 20, 1.50)
I get this error:
The column cannot be modified. [ Column name = id ]
I thought identity would auto increment the value, so can't I do either, and how can I fix it? Thanks in advance
You must explicitly specify columns in your insert
insert Inventory(Description, Quantity, Price) values ( ...)

T-SQL - In Single Stored procedure want to pass value(result) of one query to another query as input

I have a Stored procedure, in which I have to insert 3 strings into 3 different Tables at a time, each string into each of the 3 tables.
In each table, a unique primary key (rowid) would be generated on insertion of the value.
Now, the Primary Key of first two tables is the Foreign key of the Third Table which as you all know, should not be null.
Here in my SP, insertion of value and generation of RowID (PrimaryKey) is done successfully.
Now I have to pass the two primary keys(Rowids) as values/Parameters(foreignkeys) into the third table, which is returning null.
Here is my SP:-
(1st Table)
INSERT INTO [kk_admin].[FA_Master](FA_Name,FA_CSession,FA_MSession) Values
(#FA_Name,#Session_Id,#Session_Id)
SELECT #**FA_ID=FA_ID** FROM [kk_admin].[FA_Master] where FA_Name=#FA_Name
(2nd Table)
INSERT INTO [kk_admin].[Dept_Master](Dept_Name,Dept_CSession,Dept_MSession) Values
(#Dept_Name,#Session_Id,#Session_Id)
SELECT #**Dept_id=Dept_id** from [kk_admin].[Dept_Master] where Dept_Name=#Dept_Name
(3rd Table)
INSERT INTO [kk_admin].[Category_Master] (**FA_Id**,**Dept_Id**,Category_Name,Category_CSession,Category_MSession) Values (#**FA_ID**,#**Dept_Id**,#Category_Name,#Session_Id,#Session_Id)
Hope everyone understood what I have explained.
Plz Help me,
Iam running out of time.
Plz help me.
Thank You in Advance,
Brightsy
You can use an OUTPUT clause (assuming you're using SQL Server 2005) to capture the primary key for the two rows you're inserting with the first two queries. You can capture the values into a temporary table. [I previously wrote that you could use a regular variable, but that's not supported.] Example code:
CREATE TABLE #FA_Master_ID ( ID int );
CREATE TABLE #Dept_Master_ID ( ID int );
INSERT kk_admin.FA_Master ( FA_Name, FA_CSession, FA_MSession )
OUTPUT INSERTED.ID INTO #FA_Master_ID
VALUES ( #FA_Name, #Session_Id, #Session_Id );
INSERT kk_admin.Dept_Master ( Dept_Name, Dept_CSession, Dept_MSession )
OUTPUT INSERTED.ID INTO #Dept_Master_ID
VALUES ( #Dept_Name, #Session_Id, #Session_Id );
INSERT kk_admin.Category_Master ( **FA_Id**, **Dept_Id**, Category_Name, Category_CSession, Category_MSession )
SELECT **FA_Id** = ( SELECT TOP 1 ID FROM #FA_Master_ID ),
**Dept_Id** = ( SELECT TOP 1 ID FROM #Dept_Master_ID ),
Category_Name = #Category_Name,
Category_CSession = #Session_Id,
Category_MSession = #Session_Id;

Resources