Convert teradata table columns to comma seperated values - teradata

Need your help to solve a particular problem I am struggling with
I have a teradata table which looks like below.
Queryid Databasename Tablename
101 DB1 TB1
101 DB2 TB2
101 DB1 TB3
102 DB3 TB3
102 DB4 TB4
. . .
. . .
. . .
I need the output in the below format.
Queryid Newcol
101 DB1.TB1,DB2.TB2,DB1.TB3
102 DB3.TB3,DB4.TB4
. .......
. .......
I am using TD13.10 and don't have XML services and also the TDStats function is not available.
Any help would be immense..
Thanks

You can use with recursive. Although you would have to use another table to store the and in the with recursive use that table to concatenate the values by rownumber.
CREATE VOLATILE TABLE temp_tbl AS (
SELECT
queryid
,databasename ||'.'||tablename
,ROW_NUMBER() OVER (PARTITION BY queryid ORDER BY queryid) AS rnk
FROM temp
) WITH DATA PRIMARY INDEX(parent_id) ON COMMIT PRESERVE ROWS
WITH RECURSIVE test1(query,databasename,tablename,LVL)
AS
(
SELECT queryid,databasename,tablename, 1
FROM <tablename>
WHERE rnk = 1
UNION ALL
SELECT queryid, TRIM(databasename) || ', ' || TRIM(tablename),LVL+1
FROM temp_tbl INNER JOIN test1
ON queryid = query
AND temp_tbl.rn = test1.lvl+1
)
SELECT query,databasename,tablename,LVL
FROM test1
QUALIFY RANK() OVER(PARTITION BY query ORDER BY LVL DESC) = 1;

Related

SQLite insert select result multiple times

I have a database with three tables 'contacts', 'names', and 'contact_names'.
create table contacts(id integer, gid integer, sid integer);
create table names(id integer primary key, name text);
create table contact_names(fullname text);
insert into names(name) values ('Eberhard');
insert into names(name) values ('Esche');
insert into contacts values(1, (select id from names where name='Eberhard'), (select id from names where name='Esche'));
Now I want to insert the pair 'given name+surname' twice, once as 'given name+surname' and once as 'surname+given name'. What I currently have is a statment like this to generate these names:
SELECT gTable.name || ' ' || sTable.name AS name1, sTable.name || ' ' || gTable.name AS name2 FROM
(
SELECT name FROM names WHERE id=2
) AS gTable,
(
SELECT name FROM names WHERE id=1
) AS sTable;
What I am not able to perform is to insert these names now to table 'contact_names' using something like this:
INSERT INTO contact_names VALUES (name1), (name2) WITH
SELECT gTable.name || ' ' || sTable.name AS name1, sTable.name || ' ' || gTable.name AS name2 FROM
(
SELECT name FROM names WHERE id=2
) AS gTable,
(
SELECT name FROM names WHERE id=1
) AS sTable;
As result table 'contact_names' shall contain the two entries (rows) "Esche Eberhard" and "Eberhard Esche". Does anyone have a clue how to achieve this? Of course I could do the select statement twice, but I would prefer to do this with one select.
BR, Udo
PS: Maybe it is useful to explain why I want to do that. The INSERT statement for 'contact_names' shall be performed by a trigger that is called on removal of entries from 'contacts'. If I remove a row for instance with
DELETE FROM contacts WHERE id=1;
then the two name combination of that contact shall be inserted into 'contact_names' (for further handling of these names). The table will be cleared regularily after handling the removed names.
You have one row with two columns, but you want two rows with one column.
In the general case, this can be done with a common table expression and a compound query:
WITH TwoColumns(a, b) AS (
SELECT a, b FROM ... -- the original two-column query
)
INSERT INTO ...(x)
SELECT a FROM TwoColumns
UNION ALL
SELECT b FROM TwoColumns;
In this case, we don't need so many subqueries and can simplify a little:
WITH gs(gName, sName) AS (
SELECT g.name,
s.name
FROM names AS g,
names AS s
WHERE g.id = 2
AND s.id = 1
)
INSERT INTO contact_names(fullname)
SELECT gName || ' ' || sName FROM gs
UNION ALL
SELECT sName || ' ' || gName FROM gs;

SQL query for Oracle

I have 2 tables in oracle . users and profile table.
i am running following query to get results
SELECT fu.user_name ,papf.SSN
FROM apps.fnd_user fu, apps.per_all_people_f papf
WHERE fu.employee_id = papf.person_id
AND (TO_CHAR(papf.EFFECTIVE_END_DATE) IS NULL
OR papf.EFFECTIVE_END_DATE > SYSDATE )
Thing is that ,
Profile table has multiple records for SSN.
So this query is returning dupes for SSN.
I just want to get one distinct record for SSN.
for eg result is like
ABC 123
ABC 123
How do i just display unquie values for SSN
Like
ABC 123
U tried distinct but did not work
Any suggestions ?
Proper use of Distinct for your case...
SELECT Distinct fu.user_name , papf.SSN
FROM apps.fnd_user fu
, apps.per_all_people_f papf
WHERE fu.employee_id = papf.person_id
AND (TO_CHAR(papf.EFFECTIVE_END_DATE) IS NULL
OR papf.EFFECTIVE_END_DATE > SYSDATE )
Query using group by can be like below to have a single record:
SELECT fu.user_name ,
papf.SSN
FROM apps.fnd_user fu,
apps.per_all_people_f papf
WHERE fu.employee_id = papf.person_id
AND ( papf.EFFECTIVE_END_DATE IS NULL
OR papf.EFFECTIVE_END_DATE > trunc(SYSDATE) --assuming effective_end_date doesnot have timestamp with it.
)
group by fu.user_name,
papf.SSN ;

Simple Split function in SQL Server 2012 with explanation pls

I have two tables Procedures and ProcedureTypes.
Procedures has a column Type which is a varchar with the values (1, 2), (3, 4), (4, 5) etc...
ProcedureType has a primary key 'ID' 1 to 9.
ID Description
1 Drug
2 Other-Drug
etc...
ID is an integer value and Type is varchar value.
Now I need to join these two tables to show the values
ID in the Procedures table
ProcedureType in the Procedures table
Description in the ProceduresType table with the value separated by a "-".
For example if he value in Type is (1,2) the new table after join should show values in the description like (Drug-Other Drug)
I have used this query bot to no avail
SELECT * FROM dbo.[Split]((select RequestType from GPsProcedures), ',')
Can anyone tell me how to do it and why the above query is not working
with Procedures as (
select 1 as ID, '1,2,3' as Typ
),
ProcedureTypes as (
select 1 as TypeID, 'Drug' as Name
union select 2 , 'Other-Drug'
union select 3 , 'Test 3'
)
/*Get one extra column of type xml*/
,Procedures_xml as (
select id,CONVERT(xml,' <root> <s>' + REPLACE(Typ,',','</s> <s>') + '</s> </root> ') as Typ_xml
from Procedures
)
/*Convert the field string to multiple rows then join to procedure types*/
, Procdure_With_Type as (
select ID,T.c.value('.','varchar(20)') as TypeID,
ProcedureTypes.Name
from Procedures_xml
CROSS APPLY Typ_xml.nodes('/root/s') T(c)
INNER JOIN ProcedureTypes ON T.c.value('.','varchar(20)') = ProcedureTypes.TypeID
)
/*Finally, group the procedures type names by procedure id*/
select id,
STUFF((
SELECT ', ' + [Name]
FROM Procdure_With_Type inn
WHERE (Procdure_With_Type.ID = inn.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
from Procdure_With_Type
group by ID
You can't have a select statement as a parameter for a function, so instead of this:
SELECT * FROM dbo.[Split]((select RequestType from GPsProcedures), ',')
Use this:
select S.*
from GPsProcedures P
cross apply dbo.[Split](P.RequestType, ',') S

SQL Concatenate multiple rows

I'm using Teradata, I have a table like this
ID String
123 Jim
123 John
123 Jane
321 Jill
321 Janine
321 Johan
I want to query the table so I get
ID String
123 Jim, John, Jane
321 Jill, Janine, Johan
I tried partition but there can be many names.
How do I get this result. Even, to point me in the right direction would be great.
Unfortunately there's no PIVOT in Teradata (only a TD_UNPIVOT in 14.10).
If you got luck there's an aggregate UDF at your site to do a group concat (probably low possibility).
Otherwise there are two options: recursion or aggregation.
If the maximum number of rows per id is known aggregation is normally faster. It's a lot of code, but most of it is based on cut&paste.
SELECT
id,
MAX(CASE WHEN rn = 1 THEN string END)
|| MAX(CASE WHEN rn = 2 THEN ',' || string ELSE '' END)
|| MAX(CASE WHEN rn = 3 THEN ',' || string ELSE '' END)
|| MAX(CASE WHEN rn = 4 THEN ',' || string ELSE '' END)
|| ... -- repeat up to the known maximum
FROM
(
SELECT
id, string,
ROW_NUMBER()
OVER (PARTITION BY id
ORDER BY string) AS rn
FROM t
) AS dt
GROUP BY 1;
For large tables it's much more efficient when you materialize the result of the Derived Table in a Volatile Table first using the GROUP BY column as PI.
For recursion you should use a Volatile Table, too, as OLAP functions are not allowed in the recursive part. Using a view instead will repeatedly calculate the OLAP function and thus result in bad performance.
CREATE VOLATILE TABLE vt AS
(
SELECT
id
,string
,ROW_NUMBER()
OVER (PARTITION BY id
ORDER BY string DESC) AS rn -- reverse order!
,COUNT(*)
OVER (PARTITION BY id) AS cnt
FROM t
) WITH DATA
UNIQUE PRIMARY INDEX(id, rn)
ON COMMIT PRESERVE ROWS;
WITH RECURSIVE cte
(id, list, rn) AS
(
SELECT
id
,CAST(string AS VARCHAR(1000)) -- define maximum size based on maximum number of rows
,rn
FROM vt
WHERE rn = cnt
UNION ALL
SELECT
vt.id
,cte.list || ',' || vt.string
,vt.rn
FROM vt
JOIN cte
ON vt.id = cte.id
AND vt.rn = cte.rn - 1
)
SELECT id, list
FROM cte
WHERE rn = 1;
There's one problem with this approach, it might need a lot of spool which is easy to see when you omit theWHERE rn = 1.
SELECT ID,
TRIM(TRAILING ',' FROM (XMLAGG(TRIM(String)|| ',' ORDER BY String) (VARCHAR(10000)))) as Strings
FROM db.table
GROUP BY 1
SQL Server 2017+ and SQL Azure: STRING_AGG
Starting with the next version of SQL Server, we can finally concatenate across rows without having to resort to any variable or XML witchery.
STRING_AGG (Transact-SQL)
SELECT ID, STRING_AGG(String, ', ') AS Strings
FROM TableName
GROUP BY ID

Result in Pivot Structure

I am sending you the details that i am in mess.
below I am describing.
Original Table Structure.
ID Date TimeLogged(Hrs) UserName
1 10/8/2012 5.50 Bubai
2 11/8/2012 2.30 Bubai
3 10/8/2012 3.30 Bhanu
4 11/8/2012 7.30 Bhanu
I want result like below. User Name should be dynamic. May be lot of
users. User name will come from Database table.
I want to show details in Gridview(Front End).explain broadly as I am very new in development.
Date Bubai Bhanu Total
10/8/2012 5.30 3 8. 30
11/8/2012 2.30 7.30 10
Total 8 10.30 18.30
You can use a PIVOT for this, either a Static or Dynamic. You can place this code in a stored procedure and populate your datagrid with it.
Static Pivot (See SQL Fiddle with Demo) This means you will hard code all values:
select convert(char(10), dt, 101), [Bubai], [Bhanu], ([Bubai] + [Bhanu]) total
from
(
select dt, timelogged, username
from test
)x
pivot
(
sum(timelogged)
for username in ([Bubai], [Bhanu])
)p
union all
select 'total', sum([Bubai]), sum([Bhanu]), sum([Bubai] +[Bhanu])
from
(
select dt, timelogged, username
from test
)x
pivot
(
sum(timelogged)
for username in ([Bubai], [Bhanu])
)p
Dynamic Pivot (See SQL Fiddle with Demo), this will get the list of fields to transform at run-time:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#totalCol AS NVARCHAR(MAX),
#totalRow AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.username)
FROM test c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #totalCol = STUFF((SELECT distinct '+' + QUOTENAME(c.username)
FROM test c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #totalRow = STUFF((SELECT distinct ',Sum(' + QUOTENAME(c.username) + ')'
FROM test c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT convert(char(10), dt, 101), ' + #cols + ', '+#totalCol +' total from
(
select dt, timelogged, username
from test
) x
pivot
(
sum(timelogged)
for username in (' + #cols + ')
) p
union all
select ''total'', '+ #totalRow +', sum('+#totalCol+')
from
(
select dt, timelogged, username
from test
)x
pivot
(
sum(timelogged)
for username in (' + #cols + ')
)p'
execute(#query)
Both of these will produce the same results.
#NikolaMarkovinović is right, you should get the results you need using a pivot query, the problem is you have to know the values in the column you want to pivot
SELECT Date , [Bunbai] , [Bhanu] , ..., /* This names have to be known, the same as in the IN part of the PIVOT */
/* You can even do this */
[Bunbai]+[Bhanu] AS Total
FROM ( <SELECT query that produces the data> ) AS T
PIVOT ( SUM( TimeLoggedHours ) FOR UserName
IN ( [Bunbai] , [Bhanu] , ... )
/* You can't write some subquery inside the IN, columns names have to be known */
) AS pvt
, you will need to write a Dinamic Query Method in you code to, first get the names of the columns, then add them in the headers of the query and the IN part of the PIVOT.
To add the totals at the bottom just write the same query but in the query that produces the data, instead of the date select 'Total' and use UNION ALL
Hope this helps.

Resources