Remove Spaces in SQL Server table data - sql

We have the following data in table1.column1
A V John
B V S Shultz
S Hanks
K L C Gove, P S Murphy
We need to remove space between one letter words. The data after conversion should like
AV John
BVS Shultz
S Hanks
KLC Gove, PS Murphy
Is it possible to write sql query regex to clean up the whole column data once. Please guide. Thanks.

You might want to take a look at Using regular expression within a stored procedure
Is describes how to use regex in a SQL Server client.
You could also write a function using REPLACE

It is possible with T-SQL but no one would call it beautiful ...
with cNames as (
select id=cast(id as tinyint), name=cast(name as varchar(50))
from (values (1, 'A V John'), (2, 'B V S Shultz'),
(3, 'S Hanks'), (4, 'K L C Gove, P S Murphy')
) n (id, name)
),
cNumbers as (
select n=row_number() over (order by (select 1))
from (values (1),(1),(1),(1)) a (n)
cross join (values (1),(1),(1),(1)) b (n)
cross join (values (1),(1),(1),(1)) c (n)
),
cNameparts as (
select c.id, n.n, c.name,
namepart=substring(c.name,n.n,charindex(' ',c.name+' ',n.n)-n.n)
from cNames c
inner join cNumbers n
on substring(' '+c.name,n.n,1) = ' '
and n.n < len(c.name)
)
select name=
(select case when len(namepart)>1 then ' ' else '' end +
namepart +
case when right(namepart,1)=',' then ' ' else '' end
from cNameparts np
where np.id = c.id
for xml path(''))
from cNames c
order by c.id;

I was looking for an answer to a similar problem I had and this seem like it solved my issue where I had
Tom Smith
Tom Smith
Tom Smith
replace(replace(replace(NAME,' ','<>'),'><',''),'<>',' ')
will all be Tom Smith based on this link

Here's the regex you need to match the space between 2 adjacent single letters, then you'd replace that with "" (a blank):
"(?<(^| )[a-zA-Z]) (?=[a-zA-Z]( |$))"
Note that this pattern doesn't include either of the letters - it's just the space between.

Related

SQL Server dynamically change WHERE clause in a SELECT based on returned data

I'm mainly a presentation/logic tier developer and don't mess around with SQL all that much but I have a problem and am wondering if it's impossible within SQL as it's not a full programming language.
I have a field ContactID which has an CompanyID attached to it
In another table, the CompanyID is attached to CompanyName
I am trying to create a SELECT statement that returns ONE CONTACT ID and in a seperate column, an aggregate of all the Companies attached to this contact (by name).
E.G
ContactID - CompanyID - CompanyName
***********************************
1 001 Lol
1 002 Haha
1 003 Funny
2 002 Haha
2 004 Lmao
I want to return
ContactID - Companies
*********************
1 Lol, Haha, Funny
2 Haha, Lmao
I have found the logic to do so with ONE ContactID at a time:
SELECT x.ContactID, substring(
(
SELECT ', '+y.CompanyName AS [text()]
FROM TblContactCompany x INNER JOIN TblCompany y ON x.CompanyID = y.CompanyID WHERE x.ContactID = 13963
For XML PATH (''), root('MyString'), type
).value('/MyString[1]','varchar(max)')
, 3, 1000)
[OrgNames] from TblContact x WHERE x.ContactID = 13963
As you can see here, I am hardcoding in the ContactID 13963, which is neccessary to only return the companies this individual is linked to.
The issue is when I want to return this aggregate information PER ROW on a much bigger scale SELECT (on a whole table full of ContactID's).
I want to have x.ContactID = (this.ContactID) but I can't figure out how!
Failing this, could I run one statement to return a list of ContactID's, then in the same StoredProc run another statement that LOOPS through this list of ContactID's (essentially performing the second statement x times where x = no. of ContactID's)?
Any help greatly appreciated.
You want a correlated subquery:
SELECT ct.ContactID,
stuff((SELECT ', ' + co.CompanyName AS [text()]
FROM TblContactCompany cc INNER JOIN
TblCompany co
ON cc.CompanyID = co.CompanyID
WHERE cc.ContactID = ct.ContactId
For XML PATH (''), root('MyString'), type
).value('/MyString[1]', 'varchar(max)'),
1, 2, '')
[OrgNames]
from TblContact ct;
Note the where clause on the inner subquery.
I also made two other changes:
I changed the table aliases to better represent the table names. This makes queries easier to understand. (Plus, the aliases had to be changed because you were using x in the outer query and the inner query.)
I replaced the substring() with stuff(), which does exactly what you want.
You could use a table variable to store the required x.ContactID and in your main query in the WHERE clause use IN clause like below
WHERE
...
x.ContactID IN (SELECT ContactID FROM #YourTableVariable)
I guess all you need to do is to use unique table identifiers in your subquery and join the table in subquery with outer table x:
SELECT x.ContactID, substring(
(
SELECT ', '+z.CompanyName AS [text()]
FROM TblContactCompany y, TblCompany z WHERE y.CompanyID = z.CompanyID AND y.ContactId = x.ContactId
For XML PATH (''), root('MyString'), type
).value('/MyString[1]','varchar(max)')
, 3, 1000)
[OrgNames] from TblContact x
Don't loop or you will get performance problems (row by agonising row RBAR). Instead do set based queries.
This is untested but should give you an idea of how it may work:
SELECT
x.ContactID,
substring(
(SELECT ', '+y.CompanyName AS [text()]
FROM TblContactCompany y
WHERE x.CompanyID = y.CompanyID
For XML PATH (''), root('MyString'), type).value('/MyString[1]','varchar(max)')
, 3, 1000)
[OrgNames]
FROM TblContact x
And I have a feeling you can use CONCAT instead of substring

trying to concatenate a column into a comma delimited list

i have 3 tables, 1 for products and one for categories the products are assigned to. what IM trying to do is concatenate the column called stCategoryName to a single column in a comma delimited list.
Basically I have the products table containing the primary key for each product and im trying to figure out how to concatenate all the stcategoryName column next to each product so i can have a simplified return
what im trying to get is the following.
stProductID stCategoryName
123 category1,category2,category3
SELECT
dbo.StoreItemTracking.StCategoryID,
dbo.StoreItemTracking.StProductID,
dbo.StoreItemTracking.viewOrder,
dbo.StoreCategories.StCategoryName,
dbo.Store_Products.PartNumber
FROM
dbo.StoreItemTracking
INNER JOIN dbo.StoreCategories
ON dbo.StoreItemTracking.StCategoryID = dbo.StoreCategories.StCategoryID
INNER JOIN dbo.Store_Products
ON dbo.StoreItemTracking.StProductID = dbo.Store_Products.ID
Im stuck as to how to concatenate a column where the query contains 3 tables to select from.
any help greatly appreciated
Look at using coalesce to turn category into a CSV:
See example:
DECLARE #EmployeeList varchar(100)
SELECT #EmployeeList = COALESCE(#EmployeeList + ', ', '')
+ CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
SELECT #EmployeeList
You can also use CTE's or Subqueries. See:
http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=createacommadelimitedlist
Another nice and easy example:
http://www.codeproject.com/Articles/21082/Concatenate-Field-Values-in-One-String-Using-CTE-i
This:
FId FName
--- ----
2 A
4 B
5 C
6 D
8 E
with:
;WITH ABC (FId, FName) AS
(
SELECT 1, CAST('' AS VARCHAR(8000))
UNION ALL
SELECT B.FId + 1, B.FName + A.FName + ', '
FROM (And the above query will return
SELECT Row_Number() OVER (ORDER BY FId) AS RN, FName FROM tblTest) A
INNER JOIN ABC B ON A.RN = B.FId
)
SELECT TOP 1 FName FROM ABC ORDER BY FId DESC
becomes:
FName
----------------------------
A, B, C, D, E,
Don't understand how your products and categories are connected but in general I do like this to create comma separated lists.
SELECT table1.Id
,Csv
FROM table1
CROSS APPLY (
-- Double select so we can have an alias for the csv column
SELECT (SELECT ',' + table2.Name
FROM table2
WHERE table2.Id = table1.Id
FOR XML PATH('')
) AS RawCsv
) AS CA1
CROSS APPLY (
-- Trim the first comma
SELECT RIGHT(RawCsv, LEN(RawCsv) - 1) AS Csv
) AS CA2

Oracle SQL Developer 3.1.07 extra spaces between characters using listagg

I am using SQL Developer 3.1.07 on an 11g database
When I use listagg to extract multiple values from a field I get a space between each character in the results for the listagg column. The query returns all the values I expect to see, it's just the extra spaces that are driving me nuts. Any thoughts?
Here is one query that I have used, but it happens everytime I use listagg in a query:
select a.personnum Emp_ID
, a.personfullname Name
, a.companyhiredtm Hire_Date
, a.employmentstatus Status
, a.employmentstatusdt Status_Date
, h.Supervisor, h.Agency
from vp_employeev42 a
left outer join (select f.personid
, listagg (g.personcstmdatatxt, ',') within group
(order by g.customdatadefid) Supervisor
from vp_employeev42 f
left outer join personcstmdata g
on f.personid = g.personid
where f.personnum like 'T%'
and f.homelaborlevelnm3 = '1872'
and (g.customdatadefid = '1'
or g.personcstmdatatxt is null)
group by f.personid) h
on a.personid = h.personid
left outer join (select f.personid
, listagg (g.personcstmdatatxt, ',')
within group (order by g.customdatadefid) Agency
from vp_employeev42 f
left outer join personcstmdata g
on f.personid = g.personid
where f.personnum like 'T%'
and homelaborlevelnm3 = '1872'
and (g.customdatadefid = '3'
or g.personcstmdatatxt is null)
group by f.personid) h
on a.personid = h.personid
where personnum like 'T%'
and homelaborlevelnm3 = '1872'
order by personnum;
Here are the results I get:
EMP_ID,NAME,HIRE_DATE,STATUS,STATUS_DATE,SUPERVISOR,AGENCY
T98999,Lxxxxm, Lxxxn,20-SEP-12,Active,20-SEP-12,, S t a f f m a r k
T98989,Fxxxxn, Dxxxxa,10-DEC-12,Active,10-DEC-12,, S t a f f m a r k
T99989,Hxxxs, Cxxxxxa,02-OCT-12,Active,02-OCT-12,, S t a f f m a r k
T99999,Hxxxs, Dxxxn,30-JAN-12,Terminated,21-MAY-12, C x x x x x x x x x r T x x x x r, P R O L O G I S T I X
are you using UTF-16 + NVARCHAR2 by any chance? eg this:
SQL> select * from nls_database_parameters where parameter='NLS_NCHAR_CHARACTERSET';
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_NCHAR_CHARACTERSET AL16UTF16
SQL> drop table test;
Table dropped.
SQL> create table test(a nvarchar2(10));
Table created.
SQL> insert into test values ('test');
1 row created.
SQL> insert into test values ('test 2');
1 row created.
SQL> select listagg(a, ',') within group (order by 1) from test group by 1;
LISTAGG(A,',')WITHINGROUP(ORDERBY1)
--------------------------------------------------------------------------------
t e s t, t e s t 2
you could cast to a char to get round this. IF this is not acceptible, you need to raise a ticket with Oracle support.
SQL> select listagg(to_char(a),',') within group (order by 1) from test group by 1;
LISTAGG(TO_CHAR(A),',')WITHINGROUP(ORDERBY1)
--------------------------------------------------------------------------------
test,test 2
SQL>
This is currently a known bug with no fix:
Bug 13501087 11.2.0.3 RDBMS 11.2.0.3 SQL EXECUTION PRODID-5 PORTID-226
Abstract: LISTAGG RETURN STRANGE DATA
*** 12/14/11 05:12 am *** (ADD: Impact/Symptom->WRONG RESULTS )
SubComponent: SQL Analytics
===========================
DETAILED PROBLEM DESCRIPTION
============================
When using LISTAGG function with NVARCHAR , data is returned with spaces
between characters
On Oracle 12c I had the issue to with LISTAGG function. I used
ASCIISTR() to get around it:
SELECT LISTAGG(TRIM(ASCIISTR(NVL(Name,''))), ';')
WITHIN GROUP (ORDER BY NAME) AS Names
Works fine over here.

SQL: Nested SELECT with multiple values in a single field

In my SQL 2005 DB, I have a table with values stored as IDs with relationships to other tables. So in my MyDBO.warranty table, I'm storing product_id instead of product_name in order to save space. The product_name is stored in MyDBO.products.
When the marketing department pulls the demographic information, the query selects the corresponding name for each ID from related tables (trimmed down for brevity):
SELECT w1.warranty_id AS "No.",
w1.created AS "Register Date"
w1.full_name AS "Name",
w1.purchase_date AS "Purchased",
(
SELECT p1.product_name
FROM WarrDBO.products p1 WITH(NOLOCK)
WHERE p1.product_id = i1.product_id
) AS "Product Purchased",
i1.accessories
FROM WarrDBO.warranty w1
LEFT OUTER JOIN WarrDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
Now, my problem is that the "accessories" column on the warranty_info table stores several values:
No. Register Date Name Purchased Accessories
---------------------------------------------------------------------
1500 1/1/2008 Smith, John Some Product 5,7,9
1501 1/1/2008 Hancock, John Another 2,3
1502 1/1/2008 Brown, James And Another 2,9
I need to do something similar with "Accessories" that I did with "Product" and pull accessory_name from the MyDBO.accessories table using accessory_id. I'm not sure where to start, because first I'd need to extract the IDs and then somehow concatenate multiple values into a string. So each line would have "accessoryname1,accessoryname2,accessoryname3":
No. Register Date Name Purchased Accessories
---------------------------------------------------------------------
1500 1/1/2008 Smith, John Some Product Case,Bag,Padding
1501 1/1/2008 Hancock, John Another Wrap,Label
1502 1/1/2008 Brown, James And Another Wrap,Padding
How do I do this?
EDIT>> Posting my final code:
I created this function:
CREATE FUNCTION SQL_GTOInc.Split
(
#delimited varchar(50),
#delimiter varchar(1)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id INT identity(1,1), -- I use this column for numbering splitted parts
val INT
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(5)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
And updated my code accordingly:
SELECT w1.warranty_id,
i1.accessories,
(
CASE
WHEN i1.accessories <> '' AND i1.accessories <> 'NULL' AND LEN(i1.accessories) > 0 THEN
STUFF(
(
SELECT ', ' + a1.accessory
FROM MyDBO.accessories a1
INNER JOIN MyDBO.Split(i1.accessories, ',') a2
ON a1.accessory_id = a2.val
FOR XML PATH('')
), 1, 1, ''
)
ELSE ''
END
) AS "Accessories"
FROM MyDBO.warranty w1
LEFT OUTER JOIN MyDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
You could write a table valued function that simply splits comma separated string into XML and turns XML nodes to rows.
See:
http://www.kodyaz.com/articles//t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx
Join to accessories through the result of function call, and stuff the result back to comma separated list of names.
Untested code:
SELECT w1.warranty_id AS "No.",
w1.created AS "Register Date"
w1.full_name AS "Name",
w1.purchase_date AS "Purchased",
(
SELECT p1.product_name
FROM WarrDBO.products p1 WITH(NOLOCK)
WHERE p1.product_id = i1.product_id
) AS "Product Purchased",
STUFF(
(
SELECT
', ' + a.name
FROM [table-valued-function](i1.accessories) acc_list
INNER JOIN accessories a ON acc_list.id = a.id
FOR XML PATH('')
), 1, 1, ''
) AS [accessories]
FROM WarrDBO.warranty w1
LEFT OUTER JOIN WarrDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
Nothing to do with your question. Just a note that your original query can also be written, moving the subqery to a join, as:
SELECT w1.warranty_id AS "No.",
w1.created AS "Register Date"
w1.full_name AS "Name",
w1.purchase_date AS "Purchased",
p1.product_name AS "Product Purchased",
i1.accessories
FROM WarrDBO.warranty w1
INNER JOIN WarrDBO.products p1
ON p1.product_id = i1.product_id
LEFT OUTER JOIN WarrDBO.warranty_info i1
ON i1.warranty_id = w1.warranty_id
ORDER BY w1.warranty_id ASC
You just need to use the FOR XML feature of SQL Server to easily cat strings:
Example from the linked blog post:
SELECT
STUFF(
(
SELECT
' ' + Description
FROM dbo.Brands
FOR XML PATH('')
), 1, 1, ''
) As concatenated_string
To parse a field that has already been stored as comma delimited you will have to write a UDF that parses the field and returns a table which can then be used with an IN predicate in your WHERE clause. Look here for starters, and here.
It seem to be a work for a concatenate aggregate function.
In SQL it can be deployed using CLR
http://www.mssqltips.com/tip.asp?tip=2022

Using MS SQL Server 2005, how can I consolidate detail records into a single comma separated list

BACKGROUND:**I am running **MS2005. I have a MASTER table (ID, MDESC) and a DETAIL table (MID, DID, DDESC) with data as follows
1 MASTER_1
2 MASTER_2
1 L1 DETAIL_M1_L1
1 L2 DETAIL_M1_L2
1 L3 DETAIL_M1_L3
2 L1 DETAIL_M2_L1
2 L2 DETAIL_M2_L2
If I join the tables with
SELECT M.*, D.DID FROM MASTER M INNER JOIN DETAIL D on M.ID = D.MID
I get a list like the following:
1 MASTER_1 L1
1 MASTER_1 L2
1 MASTER_1 L3
2 MASTER_2 L1
2 MASTER_2 L2
QUESTION: Is there any way to use a MS SQL select statement to get the detail records into a comma separated list like this:
1 MASTER_1 "L1, L2, L3"
2 MASTER_2 "L1, L2"
You need a function:-
CREATE FUNCTION [dbo].[FN_DETAIL_LIST]
(
#masterid int
)
RETURNS varchar(8000)
AS
BEGIN
DECLARE #dids varchar(8000)
SELECT #dids = COALESCE(#dids + ', ', '') + DID
FROM DETAIL
WHERE MID = #masterid
RETURN #dids
END
Usage:-
SELECT MASTERID, [dbo].[FN_DETAIL_LIST](MASTERID) [DIDS]
FROM MASTER
Thanks to the concept in the link from Bill Karwin, it's the CROSS APPLY that makes it work
SELECT ID, DES, LEFT(DIDS, LEN(DIDS)-1) AS DIDS
FROM MASTER M1 INNER JOIN DETAIL D on M1.ID = D.MID
CROSS APPLY (
SELECT DID + ', '
FROM MASTER M2 INNER JOIN DETAIL D on M2.ID = D.MID
WHERE M1.ID = M2.ID
FOR XML PATH('')
) pre_trimmed (DIDS)
GROUP BY ID, DES, DIDS
RESULTS:
ID DES DIDS
--- ---------- ---------------
1 MASTER_1 L1, L2, L3
2 MASTER_2 L1, L2
coalesce is your friend.
declare #CSL vachar(max)
set #CSL = NULL
select #CSL = coalesce(#CSL + ', ', '') + cast(DID as varchar(8))
from MASTER M INNER JOIN DETAIL D on M.ID = D.MID
select #CSL
This will not work well for a generalized query (i.e. works great for a single master record).
You could drop this into a function... but that may not give you the performance you need/want.
This is the purpose of MySQL's GROUP_CONCAT() aggregate function. Unfortunately, it's not very easy to duplicate this function in other RDBMS brands that don't support it.
See Simulating group_concat MySQL function in Microsoft SQL Server 2005?
I think you need a function for this to work properly in recent version of SQL Server:
http://sqljunkies.com/WebLog/amachanic/archive/2004/11/10/5065.aspx?Pending=true