Converts rows in columns in vertica based on key - sql

I have table in vertica as detailed below:
key value rank
ABC 3.6138 1
ABC 1.8845 2
ABC 0.604 3
ABC -0.0351 4
ABC -0.2873 5
I want convert all the values of column- value into column separated by comma as details below:
Key value
ABC 3.6138, 1.8845, 0.604, -0.0351, -0.2873
A quick help would helpful for me a lot. Thanks in advance
Thanks.

Make use of stuff for path
SELECT key,
ConcatValue = STUFF(
(
SELECT ',' + value FROM TableName ORDER BY Rank
FOR XML PATH ('')), 1, 1, ''
)
FROM #TableName GROUP BY key

Related

STUFF doesn't work well with NULL Values and Grouping

I have table with below schema and data.
CREATE TABLE [dbo].[SearchTest]
(
[DocumentNumber] [int] NOT NULL,
[AlphaNumeric] [nvarchar](50) NULL,
[Integers] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (1, N'abc', 1)
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (2, N'abc', 1)
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (3, N'bcd', 2)
INSERT [dbo].[SearchTest] ([DocumentNumber], [AlphaNumeric], [Integers])
VALUES (4, N'bcd', 2)
GO
Table data:
I would like to do grouping using Alphanumeric and Integers column and get the DocumentNumber as comma separated value in my final result.
My final result should look like this,
Here is my query that gives the above output,
SELECT *
FROM
(SELECT
STUFF((SELECT ', ' + CAST(DocumentNumber AS VARCHAR(10)) [text()]
FROM SearchTest
WHERE AlphaNumeric = Result.Alphanumeric
OR Integers = Result.Integers
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ' ') DocumentNumbers,
COUNT(DocumentNumber) TotalDocuments,
Result.AlphaNumeric,
Result.Integers
FROM
(SELECT *
FROM SearchTest
WHERE AlphaNumeric LIKE '%b%' OR Integers = 1) AS Result
GROUP BY
Result.AlphaNumeric, Result.Integers) AS Final
However the above query breaks if I have null values in Integers column.
For example, if I have NULL value in my integer columns as shown here:
Now my query breaks and I get the wrong results in my stuff query as shown below
Grouping works fine in the above query but STUFF part which gives DocumentNumbers gives wrong result. In this case it has be 2 in first row and 1 in second row.
Here is the expected result:
| DocumentNumbers| TotalDocuments| AlphaNumeric | Integers |
+----------------+---------------+---------------+---------------+
| 2 | 1 | abc | NULL |
| 1 | 1 | abc | 1 |
| 3, 4 | 2 | bcd | 2 |
Please assist on where I'm going wrong
You need to change the WHERE clause of the inner query to a) use AND instead of OR and b) to check for NULLs too.
SELECT stuff((SELECT concat(', ', documentnumber)
FROM searchtest st2
WHERE (st2.alphanumeric = st1.alphanumeric
OR st2.alphanumeric IS NULL
AND st1.alphanumeric IS NULL)
AND (st2.integers = st1.integers
OR st2.integers IS NULL
AND st1.integers IS NULL)
FOR XML PATH('')),
1,
2,
'') documentnumbers,
count(*) totaldocuments,
alphanumeric,
integers
FROM searchtest st1
WHERE st1.alphanumeric LIKE '%b%'
OR st1.integers = 1
GROUP BY st1.alphanumeric,
st1.integers;
Following #GordonLinoff comments in the question. This can be easily achieved using STRING_AGG() provided you're using SQL Server 2017 and above. This simplifies the query as well.
Query:
SELECT *
FROM
(SELECT
STRING_AGG(Result.DocumentNumber, ', ') DocumentNumbers,
COUNT(DocumentNumber) TotalDocuments,
Result.AlphaNumeric,
Result.Integers
FROM
(SELECT *
FROM SearchTest
WHERE AlphaNumeric LIKE '%b%' OR Integers = 1) AS Result
GROUP BY
Result.AlphaNumeric, Result.Integers) AS Final
Expected Output:
I had edited the code now:
You can use the below code to attain this:
SELECT S.DocumentNumbers,COUNT(S.DocumentNumbers) AS TotalDocuments,S.AlphaNumeric,S.Integers FROM
(SELECT COALESCE(Stuff((SELECT ', ' + CAST(DocumentNumber AS VARCHAR(10))
FROM SearchTest T1
WHERE T1.AlphaNumeric=T2.AlphaNumeric AND T1.Integers=T2.Integers
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)'),1,2,' ')
,CAST (T2.DocumentNumber AS VARCHAR(20))) AS DocumentNumbers,T2.AlphaNumeric,T2.Integers
FROM SearchTest T2) S
GROUP BY DocumentNumbers,S.AlphaNumeric,S.Integers
ORDER BY S.Integers

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.

How to write different data type in one result column

For example, i have two columns in one table named tab1
1th column has type int, it is PK column with numerals
2nd one has nvarchar type.
ID Name
1 Anna
2 Vladimir
What i want:
Result
ID_Name
1 Anna
2 Vladimir
Use CONCAT function in your select:
Select Concat(ID, ' ', Name) AS ID_Name FROM tab1
your first column is of type int so you need to convert it intoor nvarchar to concatenate with name column wich is nvarchar
SELECT CONVERT(VARCHAR,ID) + ' ' + Name AS ID_Name
FROM my_table
also you can use CONCAT like this
SELECT CONCAT(ID,' ',Name) FROM my_table
select concat(column1,' ',column2) from table
in your question, it will be
select concat(ID,' ',Name) from tab1
SELECT CONVERT(VARCHAR(30),ID) + ' ' + Name AS ID_Name
FROM my_table
you can use (concat('column1','column2') as column name)
You need to use CONVERT() or CAST() string function while you want to use integer column concate with VARCHAR OR NVARCHAR column.
From SQL Server 2012 onward, you can use CONCAT() string function, which is take care of integer to string conversion.
Please check below select script.
SELECT
*
INTO #tblA
FROM
(
SELECT 1 ID,'Anna' Name UNION ALL
SELECT 2 ID,'Vladimir' Name
) A
SELECT
CONVERT(NVARCHAR(11),t.ID) + ' ' + t.Name AS ID_Name
--CONCAT(t.ID,' ',t.Name) AS ID_Name /*SQL Server 2012 Onwards*/
FROM #tblA t

SQL Server : add characters to a column

I have problem with getting SQL code: in a column I have a value with 5 or 6 characters before that values I need put character :0 and value in column must have 7 characters.
Column
------
123456
123456
12345
12345
123456
This is my code which does not work (I am using SQL Server):
Update table
set column = CONCAT( '0', ( column ) )
where LEN( + RTRIM ( column ) ) < 7
Update table
set column = CONCAT( '0', RTRIM ( column ) )
where LEN( RTRIM( column ) ) < 7
UPDATE table
SET column = '0' + column
WHERE LEN(column) = 7
My result : after my attempt, I get 0 before values but somewhere still 0 missing.
Column
-------
0123456
0123456
012345
012345
0123456
I need :
Column
-------
0123456
0123456
0012345
0012345
0123456
Thanks for updating my code
You want to left pad the value. Here is one method:
Update table
set column = RIGHT(CONCAT( REPLICATE('0', 7), ( column ) ), 7)
where LEN( + RTRIM ( column ) ) < 7;
From your examples, I am unsure whether there are multiple values in the same field. However, assuming the value for a given row must contain a single 7 digit number with 0 padding, see the examples below. Remember that the columns that stores the padded values must be a string such as VARCHAR.
UPDATE table SET column = RIGHT('0000000' + column, 7)
This will update any value to be padded with up to 7 zeros.
If your column is currently stored as an Integer rather than a String, use the following:
UPDATE table SET TextColumn = RIGHT('0000000' + CONVERT(VARCHAR, IntColumn), 7)

How can I repeat a value from one column according to a value from another column?

I have data like below - one column with a value, the second with a count:
Name Count
----- -----
John 2
Smith 3
I want an output like below - each row consisting of the value in the first column repeated n times, where n is the value of the second column:
John,John
Smith,Smith,Smith
Is there a built-in Oracle function or SQL query that could be used to achieve this? If PL/SQL is required, that would also be helpful.
Looks to me that RPAD can do it
This is NOT TESTED as I don't have an Oracle db to hand
RPAD("", (LENGTH(name)+1)*count -1, name||',')
(rpad a blank string with count copies of the string, by requiring the result to be (length(name)+1)*count -1 long. The -1 is to remove the trailing comma )
Hat tip to OracleUser for the appending-the comma bit.
I'm not familiar with oracle, but if you know how to use recursion in oracle then you might be able to convert my T-SQL code to what works for you...
Step 1. Create test table containing your sample data:
CREATE TABLE #t ([Name] VARCHAR(20), [Count] INT);
INSERT INTO #t VALUES('John',2)
INSERT INTO #t VALUES('Smith',3)
SELECT * FROM #t
This gives the following output:
Name Count
-------------------- -----------
John 2
Smith 3
Step 2. Recursive Query to get desired output:
DECLARE #Separator VARCHAR(3); SET #Separator = ','
;WITH myCTE AS (
SELECT 1 AS [Count], [Name], [Count] AS RequiredCount, [Name] AS Result FROM #t
UNION ALL
SELECT [Count] + 1, [Name], RequiredCount, CAST(Result + #Separator + [Name] AS VARCHAR(20)) FROM myCTE WHERE RequiredCount > [Count]
)
SELECT [Count], [Name], Result FROM myCTE WHERE [Count] = RequiredCount
This gives the following output:
Count Name Result
----------- -------------------- --------------------
3 Smith Smith,Smith,Smith
2 John John,John
If this doesn't make sense to you, post a comment... I'll try and explain. I'm sure someone familiar with PL/SQL can help you convert this to meet your needs.
All the best!