Use of pivot function - sql

I have the following oracle table:
Tag Value
A Test
B Test2
C Test3
D Test4
But need an output like:
A B C D
Test Test2 Test3 Test4
Where A, B, ... should be my column names. I know the pivot/unpivot function but I didn't get the right result yet.
This was my attempt but with no succes because of error: ORA-00933
SELECT *
FROM (
SELECT tag
FROM table
WHERE VALUES LIKE '%Test%'
) AS DT
PIVOT(max(value) FOR tag IN([A],[B])) AS PT

Something like that:
select * from (select tag, Value from TAB) PIVOT (max(value) for tag in ('A','B','C','D'))

Related

Find the difference of values between 2 columns after joining 2 tables on ms sql server

I have 2 tables in MS SQL Server 2019 - test1 and test2. Below are the table creation and insert statements for the 2 tables :
create table test2 (id nvarchar(10) , code nvarchar(5) , all_names nvarchar(80))
create table test3 (code nvarchar(5), name1 nvarchar(18) )
insert into test2 values ('A01', '50493', '12A2S0403-Buffalo;13A1T0101-Boston;13A2C0304-Miami')
insert into test2 values ('A02', '31278', '12A1S0205-Detroit')
insert into test2 values ('A03', '49218', '12A2S0403-Buffalo;12A1M0208-Manhattan')
insert into test3 values ('50493', 'T0101-Boston')
insert into test3 values ('49218', 'S0403-Buffalo')
insert into test3 values ('31278', 'S0205-Detroit')
I can join the 2 tables on the code column. Task is to find difference of test2.all_names and test3.name1. For example 'A01' should display the result as '12A2S0403-Buffalo;13A2C0304-Miami'.
A02 should not come as output.
The output should be :
Id | Diff_of_name
----------------------------------------
A01 | 12A2S0403-Buffalo;13A2C0304-Miami
A03 | 12A1M0208-Manhattan
Here's one possible solution, first using openjson to split your source string into rows, then using exists to check for matching values in table test3 and finally string_agg to provide the final result:
select Id, String_Agg(j.[value], ';') within group (order by j.seq) Diff_Of_Name
from test2 t2
cross apply (
select j.[value], Convert(tinyint,j.[key]) Seq
from OpenJson(Concat('["',replace(all_names,';', '","'),'"]')) j
where not exists (
select * from test3 t3
where t3.code = t2.code and j.[value] like Concat('%',t3.name1,'%')
)
)j
group by t2.Id;
Demo Fiddle
I don't like the need to normalize. However, if one must normalize, STRING_SPLIT is handy.
When done with the real work, STRING_AGG can de-normalize the data.
WITH normalized as ( -- normalize all_names in test2 to column name1
SELECT t2.id, t2.code, t2.all_names, n.value as [name1]
FROM test2 t2
OUTER APPLY STRING_SPLIT(t2.all_names, ';') n
) select * from normalized;
WITH normalized as ( -- normalize all_names in test2 to column name1
SELECT t2.id, t2.code, t2.all_names, n.value as [name1]
FROM test2 t2
OUTER APPLY STRING_SPLIT(t2.all_names, ';') n
), differenced as ( -- exclude name1 values listed in test3, ignoring leading characters
SELECT n.*
FROM normalized n
WHERE NOT EXISTS(SELECT * FROM test3 t3 WHERE t3.code = n.code AND n.name1 LIKE '%' + t3.name1)
) -- denormalize
SELECT id, STRING_AGG(name1, ';') as [Diff_of_name]
FROM differenced
group by id
order by id
id Diff_of_name
---------- ---------------------------------
A01 12A2S0403-Buffalo;13A2C0304-Miami
A03 12A1M0208-Manhattan

Possible to un-anonymize VALUES clause?

Let's say I have the following as a starting point:
select * from (values (1,'a'),(2,'b'))
Is it possible to provide column names to the value columns up-stream, for example something like:
select
col1 AS id,
col2 AS letter
from (
<anonymous values>
)
Or is it basically once you have an anonymous values clause you cannot name it.
You can use a table alias that also specifies column names:
select *
from (
values (1,'a'),(2,'b')
) as v(id, letter);
use an alias:
select * from (values (1,'a'),(2,'b')) as foo(id,txt);
id | txt
----+-----
1 | a
2 | b
(2 rows)
Absolutely
Just add the column names to the query alias
select q.id, q.col
from (values
(1,'a'),
(2,'b')
) q(id, col)

Combining two columns data into one using SQL Server 2008 R2

I have a table:
create table test
(
cola varchar(10),
colb varchar(10)
)
With some records:
cola colb
------------
A B
C D
E F
G H
I want to show the result in the following format:
columnName
----------
A
C
E
G
B
D
F
H
Attempt:
select cola+colb as columnName from test;
Gives me:
columnName
------------
AB
CD
EF
GH
You need to use the UNION operation for this task. Here's your required query.
select cola as columnName
from test
union
select colb as columnName
from test
NOTE: UNION will keep only one record in case of duplicate values between cola and colb. If you want to see the duplicate values repeated in your result, use the UNION ALL operation instead.

How can I UNPIVOT columns into rows?

Consider the following table #document_fields
id x y z
------------------
1 foo bar baz
2 one two three
3 aaa bbb ccc
4 123 456 789
The number of columns may vary, as do the column names. I need to pivot the columns into rows like this:
id field value
---------------
1 x foo
1 y bar
1 z baz
2 x one
2 y two
2 z three
.
.
3 z 789
I'm trying to understand the way pivoting in SQL Server works but can't get any further than this:
select
*
into
#document_fields_pivot
from (
select * from (
select *
from #document_fields
) t
pivot (
-- ??
) p
) tbl
Can anyone help me to finish this query / explain me pivoting? Any help is greatly appreciated.
What you want is called UNPIVOT and done like so:
select id,field,value from
#document_fields
unpivot
(
value
for field in (x,y,z)
) as u
order by id,field
Demo
DECLARE #Component table (ComponentId INT,Descr VARCHAR(10),Descr1 VARCHAR(10),Descr2 VARCHAR(10))
INSERT INTO #Component (ComponentId,Descr,Descr1,Descr2)values (1,'foo','bar','baz')
INSERT INTO #Component (ComponentId,Descr,Descr1,Descr2)values (2,'one','two','three')
INSERT INTO #Component (ComponentId,Descr,Descr1,Descr2)values (3,'aaa','bbb','ccc')
INSERT INTO #Component (ComponentId,Descr,Descr1,Descr2)values (3,'123','456','789')
select
ComponentId,Field
from #Component P
UNPIVOT
(
Field
FOR Value
IN (
[Descr],[Descr1],[Descr2]
)
) PivotTable;

SQL Query to retrieve rows having conditional values in a column

I don't know if this question has been asked previously also. If so please direct me to the link.
I have a table that has three columns name, type and date. Type can only be 4 values A, B, C and D
I want to fetch all those records which are of type A, B or C but the condition is that it should only fetch if the same name also has a type of D.
e.g. lets consider this table
Name type Date
abc A 5/7
abc B 6/7
abc D 7/7
xyz A 5/7
xyz D 6/7
lmn A 5/7
lmn B 6/7
lmn C 7/7
So the deal here I need the following result set
ABC 5/7
ABC 6/7
XYZ 5/7
Because ABC and XYZ has a type D the other records of ABC and XYZ are shown. Since lmn does not have a type D it's not included in the result set.
To test if a record exist, you can simply use where exists :
select * from mytable t1 where exists (
select * from mytable t2 where t1.Name=t2.Name and t2.type="D"
);
That's probably self explanatory but here's a reference : http://dev.mysql.com/doc/refman/5.0/en/exists-and-not-exists-subqueries.html
If you want to exclude the D records, you do this :
select * from mytable t1 where t1.type<>"D" and exists (
select * from mytable t2 where t1.Name=t2.Name and t2.type="D"
);
Try this:
SELECT Name, Date
FROM MyTable as mt
WHERE type != 'D'
AND EXISTS
(
SELECT * FROM MyTable as mt2
WHERE mt2.type = 'D' and mt2.name = mt.name
)
You are selecting all records where type is not equal to D and that have a record with a matching name where type IS equal to D
create view all_D as select name from your_table where type=D
select * from your_table where type<>D and name in (select * from all_D)
You could even make it such that instead of having that view you jut put that query in the brackets after "not in"