transpose /unpivot solution for SQL in teradata - sql

Let's say we have data as below
city | grp1| grp2| grp3| grp4|token1|token2|token3|token4
('mumbai','111111','222222','333333','444444' ,1001, 2001, 3001, 4001),
('pune','555555','666666','777777','888888', 1002, 2002, 3002, 4002);
Note that
grp1 is related to token1
grp2 is related to token2
grp3 is related to token3
grp4 is related to token4
I am looking for the below output: (8 rows)
city,grp_consolidated,token_cons
mumbai,111111,1001 |
mumbai,222222,2001 |
mumbai,333333,3001 |
mumbai,444444,4001 |
pune,555555,1002 |
pune,666666,2002 |
pune,777777,3002 |
pune,888888,4002 |
CREATE TABLE sample_table(
city VARCHAR(8),
grp1 VARCHAR(8),
grp2 VARCHAR(8),
grp3 VARCHAR(8),
grp4 VARCHAR(8),
token1 DECIMAL(31,18),
token2 DECIMAL(31,18),
token3 DECIMAL(31,18),
token4 DECIMAL(31,18)
);
INSERT INTO sample_table
VALUES
('mumbai','111111','222222','333333','444444',1001,2001,3001,4001),
('pune','555555','666666','777777','888888',1002,2002,3002,4002);
Note: Please do not provide solutions using CASE / UNION/UNION ALL

Simple task for UNPIVOT:
SELECT city, grp, token
FROM sample_table
UNPIVOT
(
(grp, token) FOR x IN (
(grp1, token1)
,(grp2, token2)
,(grp3, token3)
,(grp4, token4)
)
) AS unpvt
;

Related

how to create a table that inherits from another table in SQL 3?

i have this table salle that has 2 attributes
this is the table that inherits from salle, it's called salleCours and has 3 additional attributes.
when i run the second command in oracle 11g express in sql command line it says under 'under' : missing or invalid option.
i dont know if it's a syntax problem or something else
Create table salle(
Numero varchar(20) primary key,
Videoprojecteur char(1) ) ;
Create table salleCours UNDER salle(
Capacite number(3),
Retroprojecteur char(1),
micro char(1)) ;
You want to define an OBJECT type and then use an object-defined table:
CREATE TYPE salle_type AS OBJECT(
Numero varchar(20),
Videoprojecteur char(1)
) NOT FINAL;
CREATE TYPE salleCours_type UNDER salle_type(
Capacite number(3),
Retroprojecteur char(1),
micro char(1)
);
CREATE TABLE salle OF salle_type (
Numero PRIMARY KEY
);
Then you can insert rows of either type:
INSERT INTO salle VALUES( salle_type( 'abc', 'Y' ) );
INSERT INTO salle VALUES( salleCours_type( 'def', 'Y', 42, 'N', 'X' ) );
And, if you want the values:
SELECT s.*,
TREAT( VALUE(s) AS salleCours_type ).Capacite AS capacite,
TREAT( VALUE(s) AS salleCours_type ).Retroprojecteur AS Retroprojecteur,
TREAT( VALUE(s) AS salleCours_type ).micro AS micro
FROM salle s
Which outputs:
NUMERO | VIDEOPROJECTEUR | CAPACITE | RETROPROJECTEUR | MICRO
:----- | :-------------- | -------: | :-------------- | :----
abc | Y | null | null | null
def | Y | 42 | N | X
db<>fiddle here

Can I count the occurences for postgres array field?

I have a table postgres that uses the array type of data, it allows some magic making it possible to avoid having more tables, but the non-standard nature of this makes it more difficult to operate with for a beginner.
I would like to get some summary data out of it.
Sample content:
CREATE TABLE public.cts (
id serial NOT NULL,
day timestamp NULL,
ct varchar[] NULL,
CONSTRAINT ctrlcts_pkey PRIMARY KEY (id)
);
INSERT INTO public.cts
(id, day, ct)
VALUES(29, '2015-01-24 00:00:00.000', '{ct286,ct281}');
INSERT INTO public.cts
(id, day, ct)
VALUES(30, '2015-01-25 00:00:00.000', '{ct286,ct281}');
INSERT INTO public.cts
(id, day, ct)
VALUES(31, '2015-01-26 00:00:00.000', '{ct286,ct277,ct281}');
I would like to get the totals per array member occurence totalized, with an output like this for example:
name | value
ct286 | 3
ct281 | 3
ct277 | 1
Use Postgres function array unnest():
SELECT name, COUNT(*) cnt
FROM cts, unnest(ct) as u(name)
GROUP BY name
Demo on DB Fiddle:
| name | cnt |
| ----- | --- |
| ct277 | 1 |
| ct281 | 3 |
| ct286 | 3 |

In SQL (PSQL), how to group by partitions of rows (how to do nested group by)?

Wording of the question needs improvement, I'm not sure how to accurately describe it.
Given a table foo, count how many languages each person can speak, grouped by format. Example:
name | format | language
------+----------+------------
abe | compiled | golang
abe | compiled | c
abe | scripted | javascript
jon | scripted | ruby
jon | scripted | javascript
wut | spoken | english
(6 rows)
Result:
name | format | count
------+----------+------------
abe | compiled | 2
abe | scripted | 1
jon | scripted | 2
wut | spoken | 1
Example data can be created using:
create table foo
(
name varchar(40) not null,
format varchar(40) not null,
language varchar(40) not null
);
insert into foo
values
( 'abe', 'compiled', 'golang' ),
( 'abe', 'compiled', 'c' ),
( 'abe', 'scripted', 'javascript' ),
( 'jon', 'scripted', 'ruby' ),
( 'jon', 'scripted', 'javascript' ),
( 'wut', 'spoken', 'english' )
;
I've tried using windowing functions count(*) over (partition by format) but it doesn't squash rows, and it would require a nested window by name, and then by format, whereas count(*) ... group by name used on its own would squash the result into one row per name.
Use group by clause :
select name, format, count(*)
from foo
group by name, format;
However, if you want to go with window function then you can also do that :
select distinct name, format,
count(*) over (partition by name, format)
from foo f;

How to combine certain data from different rows based on a certain column?

I have a table that looks like this:
--------------------------------
| name | email | friend |
--------------------------------
1 | bob | bobs email | kate |
--------------------------------
2 | bob | bobs email | joe |
--------------------------------
3 | tim | tims email | eddie |
How can I create new columns (friend1, friend2, etc.) and move friends there, on the condition that name and email are the same (there might be two bobs, for instance, bob and bob with a different email).
My desired table looks like this:
-----------------------------------------------------
| name | email | friend1 | friend2 | friend3 |
-----------------------------------------------------
1 | bob | bobs email | kate | joe | |
-----------------------------------------------------
2 | tim | tims email | eddie | | |
This can't be achieved as the query you need has no static metadata (i.e. you don't know the columns) as it might change over time if a friend is added. But if you mean that you need only just three columns for friends, you can use the PIVOT command. You can use the below link as an example:
http://blogs.msdn.com/b/spike/archive/2009/03/03/pivot-tables-in-sql-server-a-simple-sample.aspx
Another solution (which is unfortunately not easily available in SQL Server) is to aggregate the friends, i.e. you will have only one column containing all friends regardless their count and separated with comma. This can be achieved using CLR function (Example: http://www.mssqltips.com/sqlservertip/2022/concat-aggregates-sql-server-clr-function/), CTE (Example: Optimal way to concatenate/aggregate strings) or FOR XML (Example: Does T-SQL have an aggregate function to concatenate strings?).
Hope this helps...
Having these sample data:
DECLARE #DataSource TABLE
(
[name] VARCHAR(12)
,[email] VARCHAR(24)
,[friend] VARCHAR(12)
)
INSERT INTO #DataSource ([name], [email], [friend])
VALUES ('bob', 'bobs email', 'kate')
,('bob', 'bobs email', 'joe')
,('tim', 'tim email', 'edie')
The following query:
SELECT DD.[name]
,DD.[email]
,Friends.[friend]
,ROW_NUMBER() OVER (PARTITION BY DD.[name], DD.[email] ORDER BY Friends.[friend]) AS [FriendNumber]
FROM
(
SELECT DISTINCT [name]
,[email]
FROM #DataSource
) DD -- Distinct Data
CROSS APPLY
(
SELECT [friend]
FROM #DataSource DS
WHERE DS.[name] = DD.[name]
AND DS.[email] = DD.[email]
) Friends
will give you:
So, you can now build want you want using pivot, but note that you need to know the maximum number of friends which a person could have:
SELECT *
FROM
(
SELECT DD.[name]
,DD.[email]
,Friends.[friend]
,'friend' + CAST(ROW_NUMBER() OVER (PARTITION BY DD.[name], DD.[email] ORDER BY Friends.[friend]) AS VARCHAR(2)) AS [FriendNumber]
FROM
(
SELECT DISTINCT [name]
,[email]
FROM #DataSource
) DD -- Distinct Data
CROSS APPLY
(
SELECT [friend]
FROM #DataSource DS
WHERE DS.[name] = DD.[name]
AND DS.[email] = DD.[email]
) Friends
) DS
PIVOT
(
MAX([friend]) FOR [FriendNumber] IN ([friend1], [friend2], [friend3])
) PVT

SQL Select unique values in 1 column

I've got problem (a little problem I suppose) and I hope, you'll help me.
I use Sybase Anywhere and here's my code:
SELECT TOP 4 Person.Id_person, Person.Name, Person.Surname, Visit.Date, Visit.Place
From Person, Visit
WHERE Visit.Id_person = Person.Id_person
ORDER BY Visit.DATE DESC
and here's the result:
3 | Paul | McDonald | 2010-01-19 | Ohio
3 | Paul | McDonald | 2010-01-18 | New York
19 | Ted | Malicky | 2009-12-24 | Tokyo
12 | Meg | Newton | 2009-10-13 | Warsaw
and I would like not to duplicate Paul McDonald, and have only first (by the date) visit. I'd like to have result like this:
3 | Paul | McDonald | 2010-01-19 | Ohio
19 | Ted | Malicky | 2009-12-24 | Tokyo
12 | Meg | Newton | 2009-10-13 | Warsaw
....
What should I do? Could you help me? :(
Here's a different way to do it using the ROW_NUMBER function to ensure that if someone has two meetings on the same day it still works:
SELECT TOP 4
Person.Id_person,
Person.Name,
Person.Surname,
T1.Date,
T1.Place
FROM
(SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Id_person ORDER BY Date DESC) AS rn
FROM Visit) AS T1
JOIN Person
ON T1.Id_person = Person.Id_person
WHERE rn = 1
ORDER BY Date DESC
Here's the result I get:
Id_person Name Surname Date Place
3 Paul McDonald 2010-01-19 Ohio
19 Ted Malicky 2009-12-24 Tokyo
12 Meg Newton 2009-10-13 Warsaw
1 Foo Bar 2009-06-03 Someplace
Here's the test data I used:
CREATE TABLE Person (Id_person INT NOT NULL, Name NVARCHAR(100) NOT NULL, Surname NVARCHAR(100) NOT NULL);
INSERT INTO Person (Id_person, Name, Surname) VALUES
(3, 'Paul', 'McDonald'),
(19, 'Ted', 'Malicky'),
(12, 'Meg', 'Newton'),
(1, 'Foo', 'Bar'),
(2, 'Baz', 'Qux');
CREATE TABLE Visit (Id_person INT NOT NULL, Date DATE NOT NULL, Place NVARCHAR(100) NOT NULL);
INSERT INTO Visit (Id_person, Date, Place) VALUES
(3, '2010-01-19', 'Ohio'),
(3, '2010-01-18', 'New York'),
(19, '2009-12-24', 'Tokyo'),
(12, '2009-10-13', 'Warsaw'),
(1, '2009-06-03', 'Someplace'),
(12, '2009-10-13', 'Anotherplace'),
(2, '2009-05-04', 'Somewhere');
Tested on SQL Server 2008, but I believe the syntax for Sybase is similar.
There is an easier way and it'll show you the most recent trip for each person as well:
SELECT TOP 4 Person.Id_person, Person.Name, Person.Surname, Visit.Date, Visit.Place
From Person, Visit
WHERE Visit.Id_person = Person.Id_person
AND (Visit.[Date] = (Select Max([Date])
From Visit Where (Person.Id_person=Visit.Id_Person)))
ORDER BY Visit.DATE DESC
I use a variant of this quite often in my work. The only caveat is that the "Date" field in the visit table is a DateTime (and, of course, that someone can't be in two places at the same time).
You can add a where not exists clause to filter out earlier visits:
SELECT TOP 4 p1.Id_person, p1.Name, p1.Surname, v1.Date, v1.Place
FROM Person p1, Visit v1
WHERE p1.Id_person = v1.Id_person
AND NOT EXISTS (
SELECT *
From Person p2, Visit v2
WHERE v2.Id_person = p2.Id_person
AND p1.Id_person = p2.Id_person
AND v2.Date > v1.Date
)
ORDER BY v1.DATE DESC
To improve readability, consider rewriting the double from as a join. For example, change:
FROM Person v1, Visit v1
WHERE v1.Id_person = p1.Id_person
into:
FROM Person p1
INNER JOIN Visit v1 ON v1.Id_person = p1.Id_person