I have a table like this:
Action Action2 Name Action3 Batch
--------------------------------------
add PL Steve add 1
add PL Steve add 3
add PL Steve add 4
add PL Steve add 5
add PL Steve add 1
add PL Steve add 3
add PL Steve add 4
add PL Steve add 5
and need to turn it into an XML document like this:
Thanks #Isaac for the data script!
for <List> elements in <Branch> node, like <Branch><List/><List/></Branch>:
CREATE TABLE #mytable
(
Action VARCHAR(10),
Action2 VARCHAR(10),
Name VARCHAR(50),
Action3 VARCHAR(10),
Batch INT
);
INSERT INTO #mytable(Action,Action2,Name,Action3,Batch)
VALUES
('add','PL','Steve','add',1),
('add','PL','Steve','add',3),
('add','PL','Steve','add',4),
('add','PL','Steve','add',5),
('add','PL','Steve','add',1),
('add','PL','Steve','add',3),
('add','PL','Steve','add',4),
('add','PL','Steve','add',5);
INSERT INTO #mytable(Action,Action2,Name,Action3,Batch)
VALUES
('update','PL','John','insert',5),
('update','PL','Paul','insert',1),
('update','PL','Chris','delete',3),
('update','PL','Mary','update',4),
('update','PL','Jane','delete',5);
select a1.Action as '#Action', s.brancexml as '*'
from
(
select distinct Action
from #mytable
) as a1
cross apply
(
select
(
select a2.Action2 AS '#Action', a2.Name as '#Name', x.listxml as '*'
from
(
select distinct Action2, Name
from #mytable AS b
where b.Action = a1.Action
) AS a2
cross apply
(
select
(
select distinct c.Action3 as '#Action', c.Batch as '#Batch'
from #mytable AS c
where c.Action = a1.Action AND c.Action2 = a2.Action2 AND c.Name = a2.Name
for xml path('List'), type
) AS listxml
) as x
for xml path('Brance'), type
) as brancexml
) as s
for xml path('Start'), root('Entries'), type
You've got a solution already, but this might be put a bit simpler:
CREATE TABLE #mytable
(
Action VARCHAR(10),
Action2 VARCHAR(10),
Name VARCHAR(50),
Action3 VARCHAR(10),
Batch INT
);
INSERT INTO #mytable(Action,Action2,Name,Action3,Batch)
VALUES
('add','PL','Steve','add',1),
('add','PL','Steve','add',3),
('add','PL','Steve','add',4),
('add','PL','Steve','add',5),
('update','PL','John','insert',5),
('update','PL','Paul','insert',1),
('update','PL','Chris','delete',3),
('update','PL','Mary','update',4),
('update','PL','Jane','delete',5);
SELECT mt1.[Action] AS [#Action]
,(
SELECT mt2.Action2 AS [#Action]
,mt2.[Name] AS [#Name]
,(
SELECT mt3.Action3 AS [#Action]
,mt3.Batch AS [#Batch]
FROM #mytable mt3
WHERE mt3.[Action]=mt1.[Action]
AND mt3.Action2=mt2.Action2
AND mt3.[Name]=mt2.[Name]
FOR XML PATH('List'),TYPE
)
FROM #mytable mt2
WHERE mt2.[Action]=mt1.[Action]
GROUP BY mt2.Action2,mt2.[Name]
FOR XML PATH('Brance'),TYPE
)
FROM #mytable mt1
GROUP BY mt1.[Action]
FOR XML PATH('Start'),ROOT('Entries');
The idea in short:
We use a cascade of correlated sub-queries, each returning one fragment of the nested structure.
Using ,TYPE will return this as XML, otherwise you would get escaped text.
Using GROUP BY allows us to return the nesting data just once.
This won't be very fast... Indexes on the columns used in WHERE will help you.
Related
I have a use case where there is a free text field and the user id in the format ab12345 (fixed) and name (dynamic) can appear anywhere in the string.
Now I need to replace the ab12345 with xxxxxxx and the names also with XXXX wherever I find them in the string.
I used:
select *
from dbo.TEST
WHERE DESCRIPTION like '%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%';
to get the user id ab12345 but I am unable to write the replace function for this since the result is dynamic.
Same with the name as well.
the following may help in redacting the userID
USE tempdb
GO
CREATE TABLE #CustComments
( CustomerID INT
, CustomerNotes VARCHAR(8000)
)
GO
INSERT dbo.#CustComments
( CustomerID
, CustomerNotes
)
VALUES
( 1, 'An infraction was raised on user id ab12345, and the name of the complainant is John')
, ( 2, 'The customer was not happy with person CD45678 and is going to ask William Jones to speak with George Hillman about this matter' )
, ( 3, 'A customer called and repeatedly mentioned the name of employee ZX98765 and assumes their name was Janet which is not correct')
SELECT * ,
PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes) start_pos,
SUBSTRING (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ) extractstring,
REPLACE(customernotes, substring (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ), 'XXXXXXX') redacted
FROM #CustComments
--TIDY UP
DROP TABLE #CustComments
if you have or can create a table of "names"...this may work
USE tempdb
GO
CREATE TABLE #CustComments (
CustomerID int,
CustomerNotes varchar(8000)
)
GO
INSERT #CustComments (CustomerID
, CustomerNotes)
VALUES (1, 'An infraction was raised on user id ab12345 , and the name of the complainant is Ann')
, (2, 'The customer was not happy with person CD45678 and is going to ask Richard Jones to speak with Todd Hillman about this matter')
, (3, 'A customer called and repeatedly mentioned the name of employee ZX98765 and assumes their name was Shana which is not correct')
CREATE TABLE #empname (
ename varchar(255) NOT NULL
)
GO
INSERT INTO #empname ([ename])
VALUES ('Zeph'), ('Ebony'), ('Felicia'), ('Benedict'), ('Ahmed'), ('Ira'), ('Julie'), ('Levi'),
('Sebastian'), ('Fiona'), ('Lamar'), ('Russell'), ('Abdul'), ('Lev'), ('Isaiah'), ('Charlotte'),
('Rowan'), ('Ivory'), ('Quinn'), ('Jordan'), ('Xantha'), ('Shana'), ('Mufutau'), ('Jessamine'),
('Desirae'), ('Yvette'), ('Odessa'), ('Ray'), ('Ori'), ('Zenaida'), ('Allegra'), ('Allistair'),
('Raymond'), ('Martena'), ('Cameron'), ('Ila'), ('Nigel'), ('Dale'), ('Emerald'), ('Guinevere'),
('Boris'), ('Dolan'), ('Ainsley'), ('Madeson'), ('Kadeem'), ('Ciaran'), ('Hop'), ('Louis'),
('Maia'), ('Hiroko'), ('Hakeem'), ('Cole'), ('Tyrone'), ('Amy'), ('Doris'), ('Keaton'),
('Carlos'), ('Richard'), ('Lysandra'), ('Beverly'), ('Hamish'), ('Demetria'), ('Eric'), ('Nayda'),
('Sydney'), ('Fritz'), ('Blaze'), ('Regina'), ('Ciara'), ('Ina'), ('Joan'), ('Risa'),
('Alea'), ('Denton'), ('Daryl'), ('Mollie'), ('Keane'), ('Jarrod'), ('Ann'), ('Juliet'),
('Germaine'), ('Alexa'), ('Zane'), ('Kiona'), ('Armand'), ('Jin'), ('Geraldine'), ('Natalie'),
('Nomlanga'), ('Todd'), ('Rajah'),('Lucian'), ('Idona'), ('Autumn'), ('Briar'),
-- add surname
('Hillman');
;
-- redact the userID std format
SELECT
CustomerID ,
--PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes) start_pos,
--SUBSTRING (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ) extractstring,
REPLACE(customernotes, substring (customernotes, (PATINDEX('%[a-zA-z][a-zA-Z][0-9][0-9][0-9][0-9][0-9]%', CustomerNotes)) ,7 ), 'XXXXXXX') ID_redacted
INTO #ID_REDACT
FROM #CustComments
-- split into rows
SELECT customerId, value
into #SPLIT
FROM #ID_REDACT
CROSS APPLY STRING_SPLIT(ID_redacted, ' ');
--redact based on join with a ""name"" table
SELECT s.customerid,
CASE
WHEN e.ename IS NULL THEN s.value
ELSE 'XXXXXXX'
END AS name_redact
INTO #NAME_REDACT
FROM #split AS s
LEFT OUTER JOIN #empname AS e
ON s.value = e.ename
SELECT customerId,
STRING_AGG(name_redact, ' ') as full_redact
INTO #RESULTS
from #NAME_REDACT
group by CustomerID
-- RESULTS WITH COMPARISON
SELECT
C.CustomerID,
C.CustomerNotes AS Original,
R.full_redact AS Redacted
FROM #CustComments AS C
INNER JOIN #RESULTS AS R
ON C.CustomerID = R.customerId
--TIDY UP
DROP TABLE #CustComments
DROP TABLE #empname
DROP TABLE #ID_REDACT
DROP TABLE #SPLIT
DROP TABLE #NAME_REDACT
DROP TABLE #RESULTS
I have 2 tables as shown:
I want to CONCAT the two tables by joining them and split them over the repetition of Row Number.
CREATE TABLE #portiontable (
PortionKey NVARCHAR(100),
RN INT,
)
CREATE TABLE #finaltable (
Value NVARCHAR(100),
RN INT,
)
INSERT INTO #finaltable (Value,RN)
VALUES ('KRM__21X0E',1),
('C',2),
('',3),
('',4),
('KRM__21X0E',1),
('C',2),
('',3),
('',4)
INSERT INTO #portiontable (PortionKey,RN)
VALUES ('100',1),
('0AD',2),
('D',3)
SELECT * FROM #finaltable f
SELECT * FROM #portiontable p
SELECT (SELECT ''+ ValuePortionKey
FROM (
SELECT f.RN,f.value,P.PortionKey, f.value + P.PortionKey AS ValuePortionKey
FROM #portiontable p
INNER JOIN #finaltable f ON p.rn = f.rn
) ft
FOR XML PATH('')) as PartSignature
DROP TABLE #portiontable
DROP TABLE #finaltable
The desired output is 2 rows:
PartSignature
KRM__21X0E100C0ADD
KRM__21X0J100K0ADD
The actual output is:
PartSignature
KRM__21X0E100C0ADDKRM__21X0J100K0ADD
Firstly, it seems that you have 2 sets of data in the #finaltable. You need another column to identify it as a set. I have added a ValueSet in the #finaltable.
And, I think your sample data does not correspond to the expected output. I have amended the sample data for #finaltable
And finally, using STRING_AGG to perform the string concatenation, you can then GROUP BY the new ValueSet
CREATE TABLE #portiontable
(
PortionKey NVARCHAR(100),
RN INT,
)
CREATE TABLE #finaltable
(
ValueSet INT,
Value NVARCHAR(100),
RN INT,
)
INSERT INTO #portiontable (PortionKey,RN)
VALUES ('100',1),
('0AD',2),
('D',3)
INSERT INTO #finaltable (ValueSet,Value,RN)
VALUES (1,'KRM__21X0E',1),
(1,'C',2),
(1,'',3),
(1,'',4),
(2,'KRM__21X0J',1),
(2,'K',2),
(2,'',3),
(2,'',4)
SELECT f.ValueSet,
STRING_AGG (f.Value + p.PortionKey, '') AS ValuePortionKey
FROM #portiontable p
INNER JOIN #finaltable f ON p.RN = f.RN
GROUP BY f.ValueSet
DROP TABLE #portiontable
DROP TABLE #finaltable
-- Result
1 KRM__21X0E100C0ADD
2 KRM__21X0J100K0ADD
I have two tables, let's call them Users and Fruit.
Users
ID Name Fruit
-------------------
1 Bob 1,3
2 Jack 3
Fruit
ID Name
-------------
1 Apple
2 Orange
3 Grape
How does one join those two tables to fill a datatable with the users choice of fruit names?
Would I need to write a stored procedure with a loop?
I am rather new to SQL Server and would be glad for any help or to be pointed in the right direction.
Created Physical tables with sample data
CREATE TABLE TempUsers
( ID INT,
Name VARCHAR(100),
Fruit VARCHAR(100)
)
INSERT INTO TempUsers
SELECT 1,'Bob' ,'1,3' UNION ALL
SELECT 2,'Jack','3'
CREATE TABLE TempFruit
( ID INT,
Name VARCHAR(100))
INSERT INTO TempFruit
SELECT 1,'Apple' UNION ALL
SELECT 2,'Orange'UNION ALL
SELECT 3,'Grape'
Create A Table-valued-Function to retrive the fruit names as comma separated
CREATE FUNCTION [dbo].[udf_GetFruitNames]
(
#vc_String nvarchar(max)
)
RETURNS #OutTable TABLE
(
Reqdata nvarchar(max)
)
AS
BEGIN
DECLARE #Temp AS TABLE
(
DATA nvarchar(max)
)
INSERT INTO #Temp
SELECT #vc_String;
DECLARE #Temp1 AS TABLE
(
DATA nvarchar(max)
)
INSERT INTO #Temp1
SELECT
STUFF((SELECT DISTINCT ','+ Name FROM
(
SELECT ID,
Name
FROm TempFruit
WHERE ID IN ( SELECT
CAST(Split.a.value('.', 'nvarchar(1000)') AS INT) AS FruitId
FROM
( SELECT
CAST( '<S>'+ REPLACE(DATA,',','</S><S>')+'</S>' AS XML) AS FruitId
FROM #Temp f
)AS A
CROSS APPLY FruitId.nodes('S') AS Split(a))
) As dt FOR XML PATH ('')),1,1,'') As FruitName
INSERT INTO #OutTable
SELECT * FROM #Temp1
RETURN
END
Sql query
SELECT ID
,Name
,uf.Reqdata AS FruitNames
FROM TempUsers u
CROSS APPLY [dbo].[udf_GetFruitNames](u.Fruit) AS uf
Or
SELECT ID
,Name
,(SELECT Reqdata FROM [dbo].[udf_GetFruitNames](u.Fruit) ) AS FruitNames
FROM TempUsers u
Result
ID Name FruitNames
---------------------
1 Bob Apple,Grape
2 Jack Grape
First of all, you need to redesign your tables. There is need for junction table, which will hold which user is connected to what fruit. It is a N:N raletionship.
So, you should create such table:
FruitUser
UserId FruitId
1 1
1 3
2 3
UserId is FK to Users table, FruitId is FK to Fruits table and both of these columns form a compoiste primary key. That's a standard approach.
Then you can use simple join to get results:
select * from users u
join FruitUser fu on u.id = fu.userid
join Fruit f on f.id = fu.fruitId
Sample Data
DECLARE #Users AS TABLE(ID INt, Name VARCHAR(100),fruit VARCHAR(100))
INSERT INTO #Users
SELECT 1,'Bob' ,'1,3' UNION ALL
SELECT 2,'Jack','3'
DECLARE #Fruit AS TABLE(ID INt, Name VARCHAR(100))
INSERT INTO #Fruit
SELECT 1,'Apple' UNION ALL
SELECT 2,'Orange'UNION ALL
SELECT 3,'Grape'
Sql Script
;WITH CTE
AS
(
SELECT UserId,
UserName ,
CAST(Split.a.value('.', 'nvarchar(1000)') AS INT) AS FruitId
FROM
( SELECT u.ID AS UserId,
u.Name AS UserName ,
CAST( '<S>'+ REPLACE(fruit,',','</S><S>')+'</S>' AS XML) AS FruitId
FROM #Fruit f
INNER JOIN #Users u
ON u.ID=f.ID
)AS A
CROSS APPLY FruitId.nodes('S') AS Split(a)
)
SELECT Userid,
UserName,
FruitId,
ft.name AS FruitName
FROM CTE c
LEFT JOIN (SELECT * FROM #Fruit) AS Ft
ON ft.ID=c.FruitId
Result
Userid UserName FruitId FruitName
------------------------------------------
1 Bob 1 Apple
1 Bob 3 Grape
2 Jack 3 Grape
For SQL Server 2014 where you can't use STRING_SPLIT , you can split the varchar using XML like following.
;WITH cte
AS (SELECT id,
name,
fruitid
FROM (SELECT *,
Cast('<X>' + Replace(F.fruit, ',', '</X><X>')
+ '</X>' AS XML) AS xmlfilter
FROM users F)F1
CROSS apply (SELECT fdata.d.value('.', 'varchar(50)') AS FruitId
FROM f1.xmlfilter.nodes('X') AS fdata(d)) O)
SELECT *
FROM cte C
INNER JOIN fruit F
ON F.id = Cast(C.fruitid AS INT)
DEMO
You can achieve the desire result without changing anything with this query
SELECT u.Id, u.Name, f.name
FROM Users u
inner join Fruit f on f.ID IN (SELECT cast(value as int)FROM STRING_SPLIT(u.fruit, ','));
Since you have sql server 2014 you have various options like CLR, XML and number functions. Best one is CLR but it's complex. So you can use this XML code.
select * from
(SELECT ID, [name],LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS fruitid
FROM
(SELECT ID,[name],CAST('<XMLRoot><RowData>' + REPLACE(fruit,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x FROM {User table})t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)) u
inner join {fruit table} f on f.id = u.fruitid
I am trying to figure out how to go about getting the values of a comma separated string that's present in one of my cells.
This is the query I current am trying to figure out in my stored procedure:
SELECT
uT.id,
uT.permissions
FROM
usersTbl AS uT
INNER JOIN
usersPermissions AS uP
/*Need to loop here I think?*/
WHERE
uT.active = 'true'
AND
uT.email = 'bbarker#thepriceisright.com'
The usersPermissions table looks like this:
And so a row in the usersTbl table looks like this for permissions:
1,3
I need to find a way to loop through that cell and get each number and place the name ****, in my returned results for the usersTbl.permissions.
So instead of returning this:
Name | id | permissions | age |
------------------------------------
Bbarker | 5987 | 1,3 | 87 |
It needs to returns this:
Name | id | permissions | age |
------------------------------------
Bbarker | 5987 | Read,Upload | 87 |
Really just replacing 1,3 with Read,Upload.
Any help would be great from a SQL GURU!
Reworked query
SELECT
*
FROM
usersTbl AS uT
INNER JOIN
usersPermissionsTbl AS uPT
ON
uPT.userId = uT.id
INNER JOIN
usersPermissions AS uP
ON
uPT.permissionId = uP.id
WHERE
uT.active='true'
AND
uT.email='bBarker#thepriceisright.com'
I agree with all of the comments... but strictly trying to do what you want, here's a way with a splitter function
declare #usersTbl table ([Name] varchar(64), id int, [permissions] varchar(64), age int)
insert into #usersTbl
values
('Bbarker',5987,'1,3',87)
declare #usersTblpermissions table (id int, [type] varchar(64))
insert into #usersTblpermissions
values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')
;with cte as(
select
u.[Name]
,u.id as UID
,p.id
,p.type
,u.age
from #usersTbl u
cross apply dbo.DelimitedSplit8K([permissions],',') x
inner join #usersTblpermissions p on p.id = x.Item)
select distinct
[Name]
,UID
,age
,STUFF((
SELECT ',' + t2.type
FROM cte t2
WHERE t.UID = t2.UID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from cte t
Jeff Moden Splitter
CREATE FUNCTION [dbo].[DelimitedSplit8K] (#pString VARCHAR(8000), #pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
enough to cover VARCHAR(8000)*/
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
GO
First, you should read Is storing a delimited list in a database column really that bad?, where you will see a lot of reasons why the answer to this question is Absolutely yes!
Second, you should add a table for user permissions since this is clearly a many to many relationship.
Your tables might look something like this (pseudo code):
usersTbl
(
Id int primary key
-- other user related columns
)
usersPermissionsTbl
(
UserId int, -- Foreign key to usersTbl
PermissionId int, -- Foreign key to permissionsTbl
Primary key (UserId, PermissionId)
)
permissionsTbl
(
Id int primary key,
Name varchar(20)
)
Once you have your tables correct, it's quite easy to get a list of comma separated values from the permissions table.
Adapting scsimon's sample data script to a correct many to many relationship:
declare #users table ([Name] varchar(64), id int, age int)
insert into #users values
('Bbarker',5987,87)
declare #permissions table (id int, [type] varchar(64))
insert into #permissions values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')
declare #usersPermissions as table (userId int, permissionId int)
insert into #usersPermissions values (5987, 1), (5987, 3)
Now the query looks like this:
SELECT u.Name,
u.Id,
STUFF(
(
SELECT ','+ [type]
FROM #permissions p
INNER JOIN #usersPermissions up ON p.id = up.permissionId
WHERE up.userId = u.Id
FOR XML PATH('')
)
, 1, 1, '') As Permissions,
u.Age
FROM #Users As u
And the results:
Name Id Permissions Age
Bbarker 5987 Read,Upload 87
You can see a live demo on rextester.
I concur with much of the advice being presented to you in the other responses. The structure you're starting with is not going to be fun to maintain and work with. However, your situation may mean you are stuck with it so maybe some of the tools below will help you.
You can parse the delimiter with charindex() as others demonstrated here- MSSQL - How to split a string using a comma as a separator
... and even better here (several functions are provided) - Split function equivalent in T-SQL?
If you still want to do it with raw inline SQL and are committed to a loop, then pair the string manipulation with a CURSOR. Cursors have their own controversies BTW. The code below will work if your permission syntax remains consistent, which it probably doesn't.
They used charindex(',',columnName) and fed the location into the left() and right() functions along with some additional string evaluation to pull values out. You should be able to piece those together with a cursor
Your query might look like this...
--creating my temp structure
declare #userPermissions table (id int, [type] varchar(16))
insert into #userPermissions (id, [type]) values (1, 'Read')
insert into #userPermissions (id, [type]) values (2, 'Write')
insert into #userPermissions (id, [type]) values (3, 'Upload')
insert into #userPermissions (id, [type]) values (4, 'Admin')
declare #usersTbl table ([Name] varchar(16), id int, [permissions] varchar(8), age int)
insert into #usersTbl ([Name], id, [permissions], age) values ('Bbarker', 5987, '1,3', 87)
insert into #usersTbl ([Name], id, [permissions], age) values ('Mmouse', 5988, '2,4', 88)
--example query
select
ut.[Name]
, (select [type] from #userPermissions where [id] = left(ut.[permissions], charindex(',', ut.[permissions])-1) )
+ ','
+ (select [type] from #userPermissions where [id] = right(ut.[permissions], len(ut.[permissions])-charindex(',', ut.[permissions])) )
from #usersTbl ut
I have DDL:
drop table names
drop table salary
create table names(
id int,
name1 varchar(50),
char1 varchar(50),
char2 varchar(50))
insert into names values (1,'name1','char1','chara'),
(2,'name2','char2','charb'),
(3,'name3','char3','charc'),
(4,'name4','char4','chard'),
(5,'name5','char5','charf');
create table salary(
id int,
salary int,
bonus int,
oldsalary int)
insert into salary values (1,500,245,354),
(2,600,345,246),
(3,60,365,334),
(4,55,545,364),
(5,25,345,374);
And have the many SQL query' s (one of them)
SELECT n.name1,
s.salary,
s.bonus,
( s.bonus + s.salary ) AS Sumsalary
FROM names n
INNER JOIN salary s
ON n.id = s.id
I want to write select query that retrieves all fieldnames from tables that used in SQL query divided by usage in this select query with some additional info mentioned in screenshot.
Format of data that I want retrieve from this query:
You could get the list of tables & columns for queries if you have access to the system views:
;WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS ns)
SELECT DISTINCT st.TEXT AS QueryText,
C.value('./#Table', 'varchar(50)') As Tab,
C.value('./#Column', 'varchar(50)') As Col
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
CROSS APPLY query_plan.nodes('//ns:ColumnReference') as T(C)
WHERE cp.ObjType = 'Adhoc' AND
St.Text Like '%s.bonus%' AND
st.Text Not Like '%WITH XMLNAMESPACES%' AND
C.value('./#Table', 'varchar(50)') IS NOT NULL
This gives the following results:
QueryText Tab Col
SELECT n.name1, ... [Names] id
SELECT n.name1, ... [Names] name1
SELECT n.name1, ... [Salary] bonus
SELECT n.name1, ... [Salary] id
SELECT n.name1, ... [Salary] salary
You could then use these results as a means of linking to sys.columns to determine which coulmns are missing etc.