Columns into row in custom format - sql

I have a sql table called UsersInAuthentication like
-----------------------
| AuthUserId | UserId |
| 1 | 4 |
| 1 | 5 |
| 1 | 8 |
-----------------------
I wish to get all users in format "(4,5,8)" as string with a single stored proc and
If possible I also wish to insert,update,delete operations with this format.
Edit:
I'm using SQL Server 2005

Beats the purpose of a table a bit. Also UPDATING this can and will get tricky.
Seems like you should either rethink the design of your table, either rethink the way you are going to use it.

Since you're using SQL Server, you can do this:
declare #list nvarchar(max)
select #list = ''
select
#list = #list +
case when #list = '' then userid
else ',' + userid end
from
UsersInAuthentication
set #list = '(' + substring(#list, 1, 999) + ')'
print #list
This is a nifty trick that builds the variable by appending each row's value to the current value of the variable. It's rather handy with table operations, and the like.
For posterity:
In MySQL, you can uses group_concat:
select
concat('(' + group_concat(userid order by userid separator ',') + ')')
from
UserInAuthentication
In Oracle, you can use a cursor:
cursor c_users is
select userid from usersinauthentication;
out_users varchar2(4000);
begin
for r_user in c_users loop
if out_users is null then
out_users:= r_user.first_Name;
else
out_users:= out_users ||', '|| r_user.first_Name;
end if;
end loop;
return out_users;

Assuming SQLServer:
--//testdata
WITH UsersInAuthentication (AuthUserId, UserId) AS (
SELECT 1, 4
UNION ALL SELECT 1, 5
UNION ALL SELECT 1, 8
)
--// real query
SELECT AuthUserId,
( SELECT cast(UserId as varchar) + ','
FROM UsersInAuthentication
ORDER BY UserID
FOR XML PATH('')
) AS UserIds
FROM UsersInAuthentication
GROUP BY AuthUserId
to get:
AuthUserId UserIds
----------- --------
1 4,5,8,

The actual queries really depend on your DBMS. Assuming Mysql:
SELECT GROUP_CONCAT(UserId) FROM UsersInAuthentication;
UPDATE UsersInAuthentication SET ... WHERE UserId IN (4,5,8);
DELETE FROM UsersInAuthentication WHERE UserId IN (4,5,8);
INSERT INTO UsersInAuthentication(UserId) VALUES (4), (5), (8);
But note that working with comma-delimited values in a database somehow misses the point.

SELECT CONCAT('(', GROUP_CONCAT(UserId SEPARATOR ','), ')')
FROM UsersInAuthentication;

Related

Replace a specific character with blank

How can I replace 'a' to blank?
`Name` `ID`
----------------------------------
`b,c,d,e,abb,a` `1`
`b,c,d,a,e,abb` `2`
`a,b,c,d,a,e,abb` `3`
One way to do it would be to add a , to the beginning and end of each Name, then replace every occurence of ',a,' with ',', then trim the result of the ,:
update table_name
set Name = trim(',' from replace(concat(',', Name, ','), ',a,', ','));
Fiddle
Or if you just want to do a select without changing the rows:
select trim(',' from replace(concat(',', Name, ','), ',a,', ',')) as Name, ID
from table_name;
To address #Iptr's comment, if there can be consecutive a such as a, a, ..., you could use STRING_SPLIT to get rows from comma-separated values, then filter out where the value is a, then STRING_AGG and group by to get the comma separated values back:
select ID, STRING_AGG(u.Value, ',') as Name
from table_name
cross apply STRING_SPLIT (Name, ',') u
where Value <> 'a'
group by ID
Fiddle
Here is a solution based on tokenization via XML/XQuery.
It will work starting from SQL Server 2012 onwards.
Steps:
We are tokenizing a string of tokens via XML.
XQuery FLWOR expression is filtering out the 'a' token.
Reverting it back to a string of tokens.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, tokens VARCHAR(1000));
INSERT INTO #tbl (tokens) VALUES
('b,c,d,e,abb,a'),
('b,c,d,a,e,abb'),
('a,b,c,d,a,e,abb');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
SELECT t.*
, REPLACE(c.query('
for $x in /root/r/text()
return if ($x = "a") then ()
else data($x)
').value('.', 'VARCHAR(MAX)'), SPACE(1), #separator) AS Result
FROM #tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(tokens, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c);
Output
+----+-----------------+-------------+
| ID | tokens | Result |
+----+-----------------+-------------+
| 1 | b,c,d,e,abb,a | b,c,d,e,abb |
| 2 | b,c,d,a,e,abb | b,c,d,e,abb |
| 3 | a,b,c,d,a,e,abb | b,c,d,e,abb |
+----+-----------------+-------------+
Try as follow:
select Replace(name, N'a', N'') as RepName , ID from yourTable
Try this.
SELECT ID,Name, REPLACE(Name, 'a', ' ')
FROM tableName;

SQL: How to split column by character count [duplicate]

This question already has answers here:
Split column into multiple columns based on character count
(3 answers)
Closed last year.
I have one column with letters. I want to split this column into chunks of three. What SQL code for Microsoft would I need? I have read 'split my a special character' but I am not sure how to create a split by value where the split is not restricted to number of columns either.
You can do :
select t.*, substring(col, 1, 3), substring(col, 4, 3), substring(col, 7, 3)
from table t
If you really want to do this dynamically, as stated in the question, and have a query that creates just as many columns as needed, then you do need dynamic SQL.
Here is a solution that uses a recusive CTE to generate the query string.
declare #sql nvarchar(max);
with cte as (
select
1 pos,
cast('substring(code, 1, 3) col1' as nvarchar(max)) q,
max(len(code)) max_pos from mytable
union all
select
pos + 1,
cast(
q
+ ', substring(code, ' + cast(pos * 3 + 1 as nvarchar(3))
+ ', 3) col'
+ cast(pos + 1 as nvarchar(3))
as nvarchar(max)),
max_pos
from cte
where pos < max_pos / 3
)
select #sql = N'select ' + q + ' from mytable'
from cte
where len(q) = (select max(len(q)) from cte);
select #sql sql;
EXEC sp_executesql #sql;
The anchor of the recursive query computes the length of the longest string in column code. Then, the recursive part generates a series of substring() expressions for each chunk of 3 characters, with dynamic column names like col1, col2 and so on. You can then (debug and) execute that query string.
Demo on DB Fiddle:
-- debug
| sql |
| :---------------------------------------------------------------------------------------------------------------------------------- |
| select substring(code, 1, 3) col1, substring(code, 4, 3) col2, substring(code, 7, 3) col3, substring(code, 10, 3) col4 from mytable |
-- results
col1 | col2 | col3 | col4
:--- | :--- | :--- | :---
ABC | DEF | GHI |
XYZ | ABC | |
JKL | MNO | PQR | STU
ABC | DEF | |
Try it like this, which does not need any generic SQL (as long as you can specify a maximum count of columns):
First we need to define a mockup scenario to simulate your issue
DECLARE #tbl TABLE(ID INT IDENTITY, YourString VARCHAR(100));
INSERT INTO #tbl VALUES ('AB')
,('ABC')
,('ABCDEFGHI')
,('XYZABC')
,('JKLMNOPQRSTU')
,('ABCDEF');
--We can set the chunk length generically. Try it with other values...
DECLARE #ChunkLength INT=3;
--The query
SELECT p.*
FROM
(
SELECT t.ID
,CONCAT('Col',A.Nmbr) AS ColumnName
,SUBSTRING(t.YourString,(A.Nmbr-1)*#ChunkLength + 1,#ChunkLength) AS Chunk
FROM #tbl t
CROSS APPLY
(
SELECT TOP((LEN(t.YourString)+(#ChunkLength-1))/#ChunkLength) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values
) A(Nmbr)
) src
PIVOT
(
MAX(Chunk) FOR ColumnName IN(Col1,Col2,Col3,Col4,Col5,Col6 /*add the maximum column count here*/)
) p;
The idea in short:
By using an APPLY call we can create a row-wise tally. This will return multiple rows per input string. The row count is defined by the computed TOP-clause.
We use the row-wise tally first to create a column Name and second as parameters in SUBSTRING().
Finally we can use PIVOT to return this as horizontal list.
One hint about generic result sets:
This might be kind of religion, but - at least in my point of view - I would prefer a fix resultset with a lot of empty columns, rather than a generically defined set. The consumer should know the result format in advance...
You might use exactly the same query as dynamically created SQL statement. The only thing you would need to change is the actual list of column names in the PIVOT's IN-clause.

Sql Procedure to count words of a given string [duplicate]

I'm trying to count how many words there are in a string in SQL.
Select ("Hello To Oracle") from dual;
I want to show the number of words. In the given example it would be 3 words though there could be more than one space between words.
You can use something similar to this. This gets the length of the string, then substracts the length of the string with the spaces removed. By then adding the number one to that should give you the number of words:
Select length(yourCol) - length(replace(yourcol, ' ', '')) + 1 NumbofWords
from yourtable
See SQL Fiddle with Demo
If you use the following data:
CREATE TABLE yourtable
(yourCol varchar2(15))
;
INSERT ALL
INTO yourtable (yourCol)
VALUES ('Hello To Oracle')
INTO yourtable (yourCol)
VALUES ('oneword')
INTO yourtable (yourCol)
VALUES ('two words')
SELECT * FROM dual
;
And the query:
Select yourcol,
length(yourCol) - length(replace(yourcol, ' ', '')) + 1 NumbofWords
from yourtable
The result is:
| YOURCOL | NUMBOFWORDS |
---------------------------------
| Hello To Oracle | 3 |
| oneword | 1 |
| two words | 2 |
Since you're using Oracle 11g it's even simpler-
select regexp_count(your_column, '[^ ]+') from your_table
Here is a sqlfiddle demo
If your requirement is to remove multiple spaces too, try this:
Select length('500 text Oracle Parkway Redwood Shores CA') - length(REGEXP_REPLACE('500 text Oracle Parkway Redwood Shores CA',
'( ){1,}', '')) NumbofWords
from dual;
Since I have used the dual table you can test this directly in your own development environment.
DECLARE #List NVARCHAR(MAX) = ' ab a
x'; /*Your column/Param*/
DECLARE #Delimiter NVARCHAR(255) = ' ';/*space*/
DECLARE #WordsTable TABLE (Data VARCHAR(1000));
/*convert by XML the string to table*/
INSERT INTO #WordsTable(Data)
SELECT Data = y.i.value('(./text())[1]', 'VARCHAR(1000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
/*Your total words*/
select count(*) NumberOfWords
from #WordsTable
where Data is not null;
/*words list*/
select *
from #WordsTable
where Data is not null
/from this Logic you can continue alon/

Replace a value in a comma separated string in SQL Server database

I'm using a SQL Server 2014 database and I have a column that contains comma-separated values such as:
1,2,3
4,5
3,6,2
4,2,8
2
What I need to do is to replace the number 2 with the number 3 (string values) in each record and not duplicate the 3 if possible. I'm not sure that this can be done unless I use a function and I'm not sure how to do it in a function.
I think I need to split a string into a table and then loop the values and put it back together with the new value. Is there an easier way? Any help is appreciated.
Expect output would therefore be:
1,3
4,5
3,6
4,3,8
3
While it is possible, I do not encourage this:
DECLARE #old AS VARCHAR(3) = '2';
DECLARE #new AS VARCHAR(3) = '3';
WITH opdata(csv) AS (
SELECT '1,22,3' UNION ALL
SELECT '1,2,3' UNION ALL
SELECT '4,5' UNION ALL
SELECT '3,6,2' UNION ALL
SELECT '4,2,8' UNION ALL
SELECT '2'
), cte1 AS (
SELECT
csv,
CASE
WHEN ',' + csv + ',' LIKE '%,' + #old + ',%' THEN
CASE
WHEN ',' + csv + ',' LIKE '%,' + #new + ',%' THEN REPLACE(',' + csv + ',', ',' + #old + ',', ',') -- new already present so just delete old
ELSE REPLACE(',' + csv + ',', ',' + #old + ',', ',' + #new + ',') -- replace old with new
END
ELSE ',' + csv + ','
END AS tmp
FROM opdata
)
SELECT
csv,
STUFF(STUFF(tmp, 1, 1, ''), LEN(tmp) - 1, 1, '') AS res
FROM cte1
Result:
csv | res
-------+-------
1,22,3 | 1,22,3
1,2,3 | 1,3
4,5 | 4,5
3,6,2 | 3,6
4,2,8 | 4,3,8
2 | 3
Note that the plethora of ',...,' is required to avoid replacing values such as 22. If you are using SQL Server 2017 you can ditch the extra CTE + STUFF and use TRIM(',' FROM ...).
This isn't going to perform particularly well, however:
WITH CTE AS (
SELECT *
FROM (VALUES ('1,2,3'),
('4,5'),
('3,6,2'),
('4,2,8'),
('2')) V(DS))
SELECT CASE WHEN DS LIKE '%3%' THEN REPLACE(REPLACE(DS,'2,',''),',2','')
WHEN DS LIKE '%2%' THEN REPLACE(DS,'2','3')
ELSE DS
END
FROM CTE;
May be you are looking something like this.
SELECT REPLACE(CASE WHEN CHARINDEX('2', '1,2,3') > 0 THEN REPLACE('1,2,3', '2','') ELSE REPLACE('1,2,3', '2','3') END, ',,',',')
I have taken a hard coded value for demonstration. You can replace'1,2,3' with column name in the table.
To update:
DECLARE #was nvarchar(2) = 2,
#willbe nvarchar(2) = 3,
#d nvarchar(1) = ','
UPDATE strings
SET string = REVERSE(
STUFF(
REVERSE(
STUFF(
CASE WHEN CHARINDEX(#d+#willbe+#d,#d+string+#d) > 0
THEN REPLACE(#d+string+#d,#d+#was+#d,#d)
ELSE REPLACE(#d+string+#d,#d+#was+#d,#d+#willbe+#d)
END,1,1,'')
),1,1,''))
Output:
1,3
4,5
3,6
4,3,8
3

How can I count the number of words in a string in Oracle?

I'm trying to count how many words there are in a string in SQL.
Select ("Hello To Oracle") from dual;
I want to show the number of words. In the given example it would be 3 words though there could be more than one space between words.
You can use something similar to this. This gets the length of the string, then substracts the length of the string with the spaces removed. By then adding the number one to that should give you the number of words:
Select length(yourCol) - length(replace(yourcol, ' ', '')) + 1 NumbofWords
from yourtable
See SQL Fiddle with Demo
If you use the following data:
CREATE TABLE yourtable
(yourCol varchar2(15))
;
INSERT ALL
INTO yourtable (yourCol)
VALUES ('Hello To Oracle')
INTO yourtable (yourCol)
VALUES ('oneword')
INTO yourtable (yourCol)
VALUES ('two words')
SELECT * FROM dual
;
And the query:
Select yourcol,
length(yourCol) - length(replace(yourcol, ' ', '')) + 1 NumbofWords
from yourtable
The result is:
| YOURCOL | NUMBOFWORDS |
---------------------------------
| Hello To Oracle | 3 |
| oneword | 1 |
| two words | 2 |
Since you're using Oracle 11g it's even simpler-
select regexp_count(your_column, '[^ ]+') from your_table
Here is a sqlfiddle demo
If your requirement is to remove multiple spaces too, try this:
Select length('500 text Oracle Parkway Redwood Shores CA') - length(REGEXP_REPLACE('500 text Oracle Parkway Redwood Shores CA',
'( ){1,}', '')) NumbofWords
from dual;
Since I have used the dual table you can test this directly in your own development environment.
DECLARE #List NVARCHAR(MAX) = ' ab a
x'; /*Your column/Param*/
DECLARE #Delimiter NVARCHAR(255) = ' ';/*space*/
DECLARE #WordsTable TABLE (Data VARCHAR(1000));
/*convert by XML the string to table*/
INSERT INTO #WordsTable(Data)
SELECT Data = y.i.value('(./text())[1]', 'VARCHAR(1000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
/*Your total words*/
select count(*) NumberOfWords
from #WordsTable
where Data is not null;
/*words list*/
select *
from #WordsTable
where Data is not null
/from this Logic you can continue alon/