SQL: create new record based on several ones - sql-server

I have a question about sql query. Assume we have one table
Reports:
+----+-------------+---------+--------+
| ID | reporter_id | subject | report |
+----+-------------+---------+--------+
| 1 | 1 | A | "OK" |
| 2 | 1 | B | "OK" |
| 3 | 1 | c |"NOT OK"|
| 4 | 2 | A | "OK" |
| 5 | 2 | C | "OK" |
+----+-------------+---------+--------+
Now, what I want to do is to select all the reports for each reporter_id and produce the following table:
+-------------+-----------------+-----------------+-----------------+
| reporter_id | report_subjectA | report_subjectB | report_subjectC |
+-------------+-----------------+-----------------+-----------------+
| 1 | "OK" | "OK" | "NOT OK" |
| 2 | "OK" | NULL | "OK" |
+-------------+-----------------+-----------------+-----------------+
Is is possible to do that?
Thank you

If subjects are unlimited, use this:
SELECT * INTO #src FROM
(VALUES
(1, 1, 'A', 'OK' ),
(2, 1, 'B', 'OK' ),
(3, 1, 'C', 'NOT OK'),
(4, 2, 'A', 'OK' ),
(5, 2, 'C', 'OK' )
)T(ID, reporter_id, subject, report)
DECLARE #columns nvarchar(MAX) = STUFF((
SELECT DISTINCT ',[Report Subject '+subject+']'
FROM #Src
FOR XML PATH('')), 1, 1, '')
DECLARE #sql nvarchar(MAX) = N'
SELECT * FROM
(
SELECT reporter_id, ''Report Subject ''+subject Title, report
FROM #Src
) T
PIVOT
(MAX(report) FOR Title IN ('+#columns+')) P'
EXEC (#sql)
If there is only 3, use simplified PIVOT:
SELECT * FROM
(
SELECT reporter_id, 'Report Subject '+subject Title, report
FROM #Src
) T
PIVOT
(MAX(report) FOR Title IN ([Report Subject A], [Report Subject B], [Report Subject C])) P
Both return:
reporter_id Report Subject A Report Subject B Report Subject C
----------- ---------------- ---------------- ----------------
1 OK OK NOT OK
2 OK NULL OK

You can as the below:
SELECT
reporter_id,
MIN(CASE WHEN UPPER([subject]) = 'A' THEN report ELSE NULL END) report_subjectA,
MIN(CASE WHEN UPPER([subject]) = 'B' THEN report ELSE NULL END) report_subjectB,
MIN(CASE WHEN UPPER([subject]) = 'C' THEN report ELSE NULL END) report_subjectC
FROM YourTable
GROUP BY
reporter_id

Try this :
declare #id int,#a char(10),#b char(10),#c char(10)
declare #reports table (reporter_id int, report_subjectA char(10), report_subjectB char(10), report_subjectC char(10))
declare c cursor for
select distinct reporter_id from reports
open c
fetch next from c into #id
while ##FETCH_STATUS=0
begin
set #a=NULL
set #B=NULL
set #c=NULL
if exists(select report from reports where subjects='A' and reporter_id =#id) set #a=(select report from reports where subjects='A' and reporter_id =#id)
if exists (select report from reports where subjects='B' and reporter_id =#id) set #b=(select report from reports where subjects='B' and reporter_id =#id)
if exists (select report from reports where subjects='C' and reporter_id =#id) set #c=(select report from reports where subjects='C' and reporter_id =#id)
insert into #reports values (#id,#a,#b,#c)
fetch next from c into #id
end
close c
deallocate c
select * from #reports

If there are only 3 subjects.
Select reporter_id, report_subjectA, report_subjectB, report_subjectC from
(
Select reporter_id, report, rep_sub = 'report_subject' + [Subject] from TableReports r
) SourceT
Pivot
(
max(report) for rep_sub in ([report_subjectA],[report_subjectB],[report_subjectC])
) AS PivotedT

Related

How to split single column into 2 columns by delimiter

I have a table with the following schema
a | b | c
qqq | www | ddd/ff
fff | ggg | xx/zz
jjj | gwq | as/we
How would I write a query so my data comes as
a | b | c_1 | c_2
qqq | www | ddd | ff
declare #t table(a varchar(20),b varchar(20),c varchar(20))
insert into #t values('qqq','www','ddd/ff')
SELECT a, b,
left(c,charindex('/',c)-1) As c_1,
right(c,charindex('/',reverse(c))-1) As c_2
FROM #t
or, if column c does not always have the format xxx/yyy, you need to validate charindex position:
declare #t table(a varchar(20),b varchar(20),c varchar(20))
insert into #t values('qqq','www','ddd/ff'), ('qqq','www','dddff')
SELECT a, b,
case when charindex('/',c) > 0 then left(c,charindex('/',c)-1) else c end As c_1,
case when charindex('/',c) > 0 then right(c,charindex('/',reverse(c))-1) else null end As c_2
FROM #t
You can use as follows :
select LEFT(name, CHARINDEX('/', name)-1) from test_table;
where it returns the left part of the string name, before slash, and the following command returns the right part, after slash.
select RIGHT(name, CHARINDEX('/', name)-1) from test_table;
I did a whole example as you can see:
create table test_table ( name varchar(50), substr1 varchar(50), substr2 varchar(50));
insert into test_table(name) values ('sub1/sub2');
update test_table set substr1 =
(select LEFT(name, CHARINDEX('/', name)-1) from test_table);
update test_table set substr2 =
(select RIGHT(name, CHARINDEX('/', name)-1) from test_table);
select * from test_table;
The result is :
name | substr1 | substr2
sub1/sub2 | sub1 | sub2
Patindex can also be used instead of Charindex
SELECT a,b,LEFT(c,PATINDEX('%/%',c)-1), RIGHT(c,PATINDEX('%/%',REVERSE(c))-1) FROM #t

Insert random Data content in SQL Server 2008

I know there are several topics on this, but none of them was suitable for me, that's why I took the chance to ask you again.
I have a table which has columns UserID, FirstName, Lastname.
I need to insert 300 000 records for each column and they have to be unique, for example:
UserID0001, John00001, Doe00001
UserID0002, John00002, Doe00002
UserID0003, John00003, Doe00003
I hope there is an easy way :)
Thank you in advance.
Best,
Lyubo
;with sequence as (
select N = row_number() over (order by ##spid)
from sys.all_columns c1, sys.all_columns c2
)
insert into [Table] (UserID, FirstName, Lastname)
select
'UserID' + right('000000' + cast(N as varchar(10)), 6),
'John' + right('000000' + cast(N as varchar(10)), 6),
'Doe' + right('000000' + cast(N as varchar(10)), 6)
from sequence where N <= 300000
You could use the ROW_NUMBER function to generate different numbers like this:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.users(
Id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
user_id VARCHAR(20),
first_name VARCHAR(20),
last_name VARCHAR(20)
);
GO
DECLARE #NoOfRows INT = 7;
INSERT INTO dbo.users(user_id, first_name, last_name)
SELECT 'User_'+n, 'John_'+n, 'Doe_'+n
FROM(
SELECT REPLACE(STR(ROW_NUMBER()OVER(ORDER BY (SELECT NULL))),' ','0') n FROM(
select TOP(#NoOfRows) 1 x from sys.objects A,sys.objects B,sys.objects C,sys.objects D,sys.objects E,sys.objects F,sys.objects G
)X
)N
Query 1:
SELECT * FROM dbo.users
Results:
| ID | USER_ID | FIRST_NAME | LAST_NAME |
-----------------------------------------------------------
| 1 | User_0000000001 | John_0000000001 | Doe_0000000001 |
| 2 | User_0000000002 | John_0000000002 | Doe_0000000002 |
| 3 | User_0000000003 | John_0000000003 | Doe_0000000003 |
| 4 | User_0000000004 | John_0000000004 | Doe_0000000004 |
| 5 | User_0000000005 | John_0000000005 | Doe_0000000005 |
| 6 | User_0000000006 | John_0000000006 | Doe_0000000006 |
| 7 | User_0000000007 | John_0000000007 | Doe_0000000007 |
Just change the #NoOfRows to 300000 to get the number of rows you are looking for.
I've adapted a script found in this article:
DECLARE #RowCount INT
DECLARE #RowString VARCHAR(14)
DECLARE #First VARCHAR(14)
DECLARE #LAST VARCHAR(14)
DECLARE #ID VARCHAR(14)
SET #ID = 'UserID'
SET #First = 'John'
SET #Last = 'Doe'
SET #RowCount = 1
WHILE #RowCount < 300001
BEGIN
SET #RowString = CAST(#RowCount AS VARCHAR(10))
SET #RowString = REPLICATE('0', 6 - DATALENGTH(#RowString)) + #RowString
INSERT INTO TestTableSize (
UserID
,FirstName
,LastName
)
VALUES
(#ID + #RowString
, #First + #RowString
, #Last + #RowString)
SET #RowCount = #RowCount + 1
END

Find specific word in column

I am using SQL Server 2008.
My tables are :
Location
------------------------
Id | LocationName
------------------------
1 | Bodakdev
2 | Thaltej Road
3 | Andheri East
4 | Noida Sector 2
Company
--------------------------------------------------------------------------
CId | Address | LocationId
--------------------------------------------------------------------------
11 | 301, GNFC Infotower, Bodakdev, | NULL
12 | 307/308,Arundeep Complex | NULL
13 | 7 Krishana Dyeing Compund, Nagardas rd., Andheri | NULL
14 | B-23 ,Ground Floor,Sector 2 | NULL
--------------------------------------------------------------------------
Currently LocationId in the Company table are null. If Address contains any location name then update LocationId.
For example, Address of CID - 11 contains Bodakdev then update LocationId 1, second example, Address of CID - 13 contains Andheri word then update LocationId 3.
Required output :
CId | Address | LocationId
--------------------------------------------------------------------------
11 | 301, GNFC Infotower, Bodakdev, | 1
12 | 307/308,Arundeep Complex | NULL
13 | 7 Krishana Dyeing Compund, Nagardas rd., Andheri | 3
14 | B-23 ,Ground Floor,Sector 2 | 4
--------------------------------------------------------------------------
I have tried using below query
SELECT
(LEN(Address) - LEN(REPLACE(Address, LocationName, '')) ) / LEN(LocationName)
if Address contains Location Name then it will return number of occurrences otherwise it return 0.
But it will not give correct output. How can I do this? Thanks. Any suggestion would be appreciated.
Try following Query :
1.STEP1 : make one function which can split the sting by any character and return the output in table format .
CREATE FUNCTION [dbo].[fnSplit](
#sInputList VARCHAR(8000) -- List of delimited items
, #sDelimiter VARCHAR(8000) = ',' -- delimiter that separates items
) RETURNS #List TABLE (item VARCHAR(8000))
BEGIN
DECLARE #sItem VARCHAR(8000)
WHILE CHARINDEX(#sDelimiter,#sInputList,0) <> 0
BEGIN
SELECT
#sItem=RTRIM(LTRIM(SUBSTRING(#sInputList,1,CHARINDEX(#sDelimiter,#sInputList,0)-1))),
#sInputList=RTRIM(LTRIM(SUBSTRING(#sInputList,CHARINDEX(#sDelimiter,#sInputList,0)+LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem
END
IF LEN(#sInputList) > 0
INSERT INTO #List SELECT #sInputList -- Put the last item in
RETURN
END
2.STEP2 : use following query to get your desire output .
DECLARE #LOCATION AS TABLE (ID INT ,NAME VARCHAR(MAX))
DECLARE #COMPANY AS TABLE (CID INT , ADDRESS VARCHAR(MAX) , LOCATIONID INT)
INSERT INTO #LOCATION VALUES(1,'Bodakdev')
INSERT INTO #LOCATION VALUES(2,'Thaltej Road')
INSERT INTO #LOCATION VALUES(3,'Andheri East')
INSERT INTO #LOCATION VALUES(4,'Noida Sector 2')
INSERT INTO #COMPANY VALUES(11,'301, GNFC Infotower, Bodakdev,' , NULL)
INSERT INTO #COMPANY VALUES(12,'307/308,Arundeep Complex' , NULL)
INSERT INTO #COMPANY VALUES(11,'7 Krishana Dyeing Compund, Nagardas rd., Andheri' , NULL)
INSERT INTO #COMPANY VALUES(11,'B-23 ,Ground Floor,Sector 2' , NULL)
UPDATE #Company
SET
LOCATIONID = B.ID
FROM #COMPANY AS A , #LOCATION AS B
WHERE
1 = CASE WHEN
(
SELECT COUNT(*)
FROM FNSPLIT(B.NAME , ' ')
WHERE A.ADDRESS LIKE '%' + ITEM + '%'
) > 0 THEN 1 ELSE 0 END
This is the one way to do it . we can do it using full text searching also.

How to replace a functional (many) OUTER APPLY (SELECT * FROM)

Applies to Microsoft SQL Server 2008 R2.
The problem is
If we have a few dozen Outer Apply (30) then they begin to work pretty slowly. In the middle of the Outer Apply I have something more complicated than a simple select, a view.
Details
I'm writing a sort of attributes assigned to tables (in the database). Generally, a few tables, holds a reference to a table of attributes (key, value).
Pseudo structure looks like this:
DECLARE #Lot TABLE (
LotId INT PRIMARY KEY IDENTITY,
SomeText VARCHAR(8))
INSERT INTO #Lot
OUTPUT INSERTED.*
VALUES ('Hello'), ('World')
DECLARE #Attribute TABLE(
AttributeId INT PRIMARY KEY IDENTITY,
LotId INT,
Val VARCHAR(8),
Kind VARCHAR(8))
INSERT INTO #Attribute
OUTPUT INSERTED.* VALUES
(1, 'Foo1', 'Kind1'), (1, 'Foo2', 'Kind2'),
(2, 'Bar1', 'Kind1'), (2, 'Bar2', 'Kind2'), (2, 'Bar3', 'Kind3')
LotId SomeText
----------- --------
1 Hello
2 World
AttributeId LotId Val Kind
----------- ----------- -------- --------
1 1 Foo1 Kind1
2 1 Foo2 Kind2
3 2 Bar1 Kind1
4 2 Bar2 Kind2
5 2 Bar3 Kind3
I can now run a query such as:
SELECT
[l].[LotId]
, [SomeText]
, [Oa1].[AttributeId]
, [Oa1].[LotId]
, 'Kind1Val' = [Oa1].[Val]
, [Oa1].[Kind]
, [Oa2].[AttributeId]
, [Oa2].[LotId]
, 'Kind2Val' = [Oa2].[Val]
, [Oa2].[Kind]
, [Oa3].[AttributeId]
, [Oa3].[LotId]
, 'Kind3Val' = [Oa3].[Val]
, [Oa3].[Kind]
FROM #Lot AS l
OUTER APPLY(SELECT * FROM #Attribute AS la WHERE la.[LotId] = l.[LotId] AND la.[Kind] = 'Kind1') AS Oa1
OUTER APPLY(SELECT * FROM #Attribute AS la WHERE la.[LotId] = l.[LotId] AND la.[Kind] = 'Kind2') AS Oa2
OUTER APPLY(SELECT * FROM #Attribute AS la WHERE la.[LotId] = l.[LotId] AND la.[Kind] = 'Kind3') AS Oa3
LotId SomeText AttributeId LotId Kind1Val Kind AttributeId LotId Kind2Val Kind AttributeId LotId Kind3Val Kind
----------- -------- ----------- ----------- -------- -------- ----------- ----------- -------- -------- ----------- ----------- -------- --------
1 Hello 1 1 Foo1 Kind1 2 1 Foo2 Kind2 NULL NULL NULL NULL
2 World 3 2 Bar1 Kind1 4 2 Bar2 Kind2 5 2 Bar3 Kind3
The simple way to get the pivot table of attribute values ​​and results for Lot rows that do not have attribute such a Kind3.
I know Microsoft PIVOT and it is not simple and do not fits here.
Finally, what will be faster and will give the same results?
In order to get the result you can unpivot and then pivot the data.
There are two ways that you can perform this. First, you can use the UNPIVOT and the PIVOT function:
select *
from
(
select LotId,
SomeText,
col+'_'+CAST(rn as varchar(10)) col,
value
from
(
select l.LotId,
l.SomeText,
cast(a.AttributeId as varchar(8)) attributeid,
cast(a.LotId as varchar(8)) a_LotId,
a.Val,
a.Kind,
ROW_NUMBER() over(partition by l.lotid order by a.attributeid) rn
from #Lot l
left join #Attribute a
on l.LotId = a.LotId
) src
unpivot
(
value
for col in (attributeid, a_Lotid, val, kind)
) unpiv
) d
pivot
(
max(value)
for col in (attributeid_1, a_LotId_1, Val_1, Kind_1,
attributeid_2, a_LotId_2, Val_2, Kind_2,
attributeid_3, a_LotId_3, Val_3, Kind_3)
) piv
See SQL Fiddle with Demo.
Or starting in SQL Server 2008+, you can use CROSS APPLY with a VALUES clause to unpivot the data:
select *
from
(
select LotId,
SomeText,
col+'_'+CAST(rn as varchar(10)) col,
value
from
(
select l.LotId,
l.SomeText,
cast(a.AttributeId as varchar(8)) attributeid,
cast(a.LotId as varchar(8)) a_LotId,
a.Val,
a.Kind,
ROW_NUMBER() over(partition by l.lotid order by a.attributeid) rn
from #Lot l
left join #Attribute a
on l.LotId = a.LotId
) src
cross apply
(
values ('attributeid', attributeid),('LotId', a_LotId), ('Value', Val), ('Kind', Kind)
) c (col, value)
) d
pivot
(
max(value)
for col in (attributeid_1, LotId_1, Value_1, Kind_1,
attributeid_2, LotId_2, Value_2, Kind_2,
attributeid_3, LotId_3, Value_3, Kind_3)
) piv
See SQL Fiddle with Demo.
The unpivot process takes the multiple columns for each LotID and SomeText and converts it into rows giving the result:
| LOTID | SOMETEXT | COL | VALUE |
--------------------------------------------
| 1 | Hello | attributeid_1 | 1 |
| 1 | Hello | LotId_1 | 1 |
| 1 | Hello | Value_1 | Foo1 |
| 1 | Hello | Kind_1 | Kind1 |
| 1 | Hello | attributeid_2 | 2 |
I added a row_number() to the inner subquery to be used to create the new column names to pivot. Once the names are created the pivot can be applied to the new columns giving the final result
This could also be done using dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+'_'+rn)
from
(
select
cast(ROW_NUMBER() over(partition by l.lotid order by a.attributeid) as varchar(10)) rn
from Lot l
left join Attribute a
on l.LotId = a.LotId
) t
cross apply (values ('attributeid', 1),
('LotId', 2),
('Value', 3),
('Kind', 4)) c (col, so)
group by col, rn, so
order by rn, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT LotId,
SomeText,' + #cols + '
from
(
select LotId,
SomeText,
col+''_''+CAST(rn as varchar(10)) col,
value
from
(
select l.LotId,
l.SomeText,
cast(a.AttributeId as varchar(8)) attributeid,
cast(a.LotId as varchar(8)) a_LotId,
a.Val,
a.Kind,
ROW_NUMBER() over(partition by l.lotid order by a.attributeid) rn
from Lot l
left join Attribute a
on l.LotId = a.LotId
) src
cross apply
(
values (''attributeid'', attributeid),(''LotId'', a_LotId), (''Value'', Val), (''Kind'', Kind)
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
All three versions will give the same result:
| LOTID | SOMETEXT | ATTRIBUTEID_1 | LOTID_1 | VALUE_1 | KIND_1 | ATTRIBUTEID_2 | LOTID_2 | VALUE_2 | KIND_2 | ATTRIBUTEID_3 | LOTID_3 | VALUE_3 | KIND_3 |
-----------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | Hello | 1 | 1 | Foo1 | Kind1 | 2 | 1 | Foo2 | Kind2 | (null) | (null) | (null) | (null) |
| 2 | World | 3 | 2 | Bar1 | Kind1 | 4 | 2 | Bar2 | Kind2 | 5 | 2 | Bar3 | Kind3 |

Merge multiple rows into a single row

I would like to know the best approach to merge data from the following rows into a single row in another view.
These are the results as they are currently displayed;
Type_ID | Client_ID | PBX_Vendor |
127 | 090820006311404926326C | Aastra |
127 | 090820006311404926326C | Ericsson |
127 | 111012237401404926326C | Aastra |
127 | 120209287521404926326C | Aastra |
127 | 120209287521404926326C | Alcatel |
The following is how I would like to see the data;
Type_ID | Client_ID | PBX_Vendor |
127 | 090820006311404926326C | Aastra, Ericsson |
127 | 111012237401404926326C | Aastra |
127 | 120209287521404926326C | Aastra, Alcatel |
Basically, there are multiple PBX Vendors associated with a Client ID. I need this display in a single row for a helpdesk system.
I have attempted this already with CONCAT, but all I end up with is a single row with over 100 vendors in it that are not specific to a Client_ID.
Any help with be very much appreciated!
Here's A way to do it (also works with 2005):
Table
DECLARE #table TABLE
(
[Type_ID] INT,
[Client_ID] VARCHAR(50),
[PBX_Vendor] VARCHAR(50)
)
Data
INSERT INTO #table
SELECT 127,
'090820006311404926326C',
'Aastra'
UNION ALL
SELECT 127,
'090820006311404926326C',
'Ericsson'
UNION ALL
SELECT 127,
'111012237401404926326C',
'Aastra'
UNION ALL
SELECT 127,
'120209287521404926326C',
'Aastra'
UNION ALL
SELECT 127,
'120209287521404926326C',
'Alcatel'
Query
SELECT [Type_ID],
[Client_ID],
(
SELECT STUFF((
SELECT ',' + [PBX_Vendor]
FROM #table
WHERE [Client_ID] = tbl.[Client_ID]
AND [Type_ID] = tbl.[Type_ID]
GROUP BY [PBX_Vendor]
ORDER BY [PBX_Vendor]
FOR
XML PATH('')
), 1, 1, '')
) PBX_Vendor
FROM #table tbl
GROUP BY [Type_ID],
[Client_ID]
Result
Type_ID Client_ID PBX_Vendor
127 090820006311404926326C Aastra,Ericsson
127 111012237401404926326C Aastra
127 120209287521404926326C Aastra,Alcatel
Dan, I have managed to get this working using your original Ideas with some modifications. Although I cannot save it as a view as I understand that you cannot save DECLARES as VIEWS;
DECLARE #table TABLE
(
[Type_ID] INT,
[Client_ID] VARCHAR(50),
[PBX_Vendor] VARCHAR(50)
)
INSERT INTO #table
SELECT dbo.AMGR_User_Fields_Tbl.Type_Id, dbo.AMGR_User_Fields_Tbl.Client_Id, dbo.AMGR_User_Field_Defs_Tbl.Description AS PBX_Vendor
FROM dbo.AMGR_User_Fields_Tbl INNER JOIN
dbo.AMGR_User_Field_Defs_Tbl ON dbo.AMGR_User_Fields_Tbl.Type_Id = dbo.AMGR_User_Field_Defs_Tbl.Type_Id AND
dbo.AMGR_User_Fields_Tbl.Code_Id = dbo.AMGR_User_Field_Defs_Tbl.Code_Id
WHERE (dbo.AMGR_User_Fields_Tbl.Type_Id = 127)
SELECT [Type_ID],
[Client_ID],
(
SELECT STUFF((
SELECT ', ' + [PBX_Vendor]
FROM #table
WHERE [Client_ID] = tbl.[Client_ID]
AND [Type_ID] = tbl.[Type_ID]
GROUP BY [PBX_Vendor]
ORDER BY [PBX_Vendor]
FOR
XML PATH('')
), 1, 1, '')
) PBX_Vendor
FROM #table tbl
GROUP BY [Type_ID],
[Client_ID]

Resources