SQL, Merge data into one row - sql

I have the following query to get a specific Area:
Select
Id, Name
from
Area
Where
(Id = 1)
Each Area can have 2 files in AreaFiles table, so I changed the query to:
SELECT
Area.Id, Area.Name, AreaFiles.FileName
FROM
AreaFiles
INNER JOIN
Area ON AreaFiles.AreaId = Area.Id
WHERE
(Area.Id = 1)
The result is:
Id | Name | FileName
--- ------ ----------
1 abc file1.jpg
1 abc file2.jpg
I want to merge these rows into one row to get this:
Id | Name | FileName1 | FileName2
--- ------ ------------------------
1 abc file1.jpg file1.jpg
Is it possible?

To achieve this, you will need to use PIVOT
See here for a good example - http://www.codeproject.com/Tips/500811/Simple-Way-To-Use-Pivot-In-SQL-Query
So for your eample here, it would be
SELECT ID,NAME, [1] AS FILENAME1, [2] AS FILENAME2 FROM
(SELECT ID, FILENAME, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS ROWNUM) FROM AREA A
PIVOT ((MAX(ID) FOR ROWNUM IN [1],[2])) AS PVT

for that you need to use with clause
with first as (select id,name,filename from area),
second as (select id,name,filename from area)
SELECT first.id,first.name,first.filename,second.filename
FROM first,second
where first.id=second.id;

Related

SQL Pivot based on condition

Friends, I have a pivot question where in I have to convert a row into column based on certain condition. here is the sample data -
id adj_product_type dti_adj
-- ---------------- -------
1 BPM DTI_GT45_LTE50
1 BPM DTI_GT45
1 BPS ABC
Expected output
id adj_product_type dti_adj adj_product_type_2 dti_adj_2
-- ---------------- ------- ------------------ ---------
1 BPM DTI_GT45_LTE50 BPS ABC
1 BPM DTI_GT45
Requirement: Convert the BPS row as new columns.
Assumptions: There could be more than two rows when adj_product_type=BPM. There will only be one row with adj_product_type=BPS.
Sample data:
create table abc1 (id number,adj_product_type varchar2(100), dti_adj varchar2(100));
insert into abc1 values(1,'BPM','DTI_GT45_LTE50');
insert into abc1 values(1,'BPM','DTI_GT45');
insert into abc1 values(1,'BPS','ABC');
I tried using LEAD but it works only when there is only one row for BPM, any help is appreciated.
select
id,
adj_product_type,
dti_adj,
lead(adj_product_type,1) over (partition by id order by adj_product_type) as adj_product_type_2,
lead(dti_adj,1) over (partition by id order by adj_product_type) as dti_adj_2,
rank() over(partition by id order by adj_product_type) as rnk
from
abc1
I suggest that you run a query made up of two sub-queries
An initial query that gets the BPM data
Then LEFT OUTER JOIN it to a second query with BPS data
However, you cannot just join on id (as you have found) instead, I suggest adding a ROW_NUMBER() value to each row in BPM data - so, for example, the first row in BPM data for id = 1 will have row_number = 1, then the second has row_number = 2, etc. Then join the BPS data onto the rows with row_number = 1.
Here is a db<>fiddle (admittedly in SQL Server) showing the logic.
Note that with your data, there isn't a clear 'sort' value that allows you to assign as the 'first' row. I've just done it based on dti_adj.
SELECT BPM_info.*, BPS_info.adj_product_type_2, BPS_info.dti_adj_2
FROM
(select
id,
adj_product_type,
dti_adj,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY dti_adj) AS rn
from
abc1
where
adj_product_type = 'BPM'
) AS BPM_info
LEFT OUTER JOIN
(select
id,
adj_product_type AS adj_product_type_2,
dti_adj AS dti_adj_2
from
abc1
where
adj_product_type = 'BPS'
) AS BPS_info ON BPM_info.id = BPS_info.id AND BPM_info.rn = 1;
Results
id adj_product_type dti_adj rn adj_product_type_2 dti_adj_2
1 BPM DTI_GT45 1 BPS ABC
1 BPM DTI_GT45_LTE50 2 null null

Where clause to select rows with only unique values

firstly let me describe you my problem. I need to ignore all repeated values in my select query. So for example if I have something like that:
| Other columns| THE COLUMN I'm working with |
| ............ | Value 1 |
| ............ | Value 2 |
| ............ | Value 2 |
I'd like to get the result containing only the row with "Value 1"
Now because of the specifics of my task I need to validate it with subquery.
So I've figured out something like this:
NOT EXISTS (SELECT 1 FROM TABLE fpd WHERE fpd.value = fp.value HAVING count(*) > 2)
It works like I want, but I'm aware of it being slow. Also I've tried putting 1 instead of 2 in HAVING comprassion, but it just returns zero results. Could you explain where does the 2 value come from?
I would suggest window functions:
select t.*
from (select t.*, count(*) over (partition by value) as cnt
from fpd t
) t
where cnt = 1;
Alternatively, you can use not exists with a primary key:
where not exists (select 1
from fpd fpd2
where fpd2.value = fp.value and
fpd2.primarykey <> fp.primarykey
)
SELECT DISTINCT myColumn FROM myTable

Splitting string and sorting in postgresql

I have a table in postgresql with a text column that has values like this:
column
-----------
CA;TB;BA;CB
XA;VA
GA;BA;LA
I want to sort the elements that are in each value, so that the query results like this:
column
-----------
BA;CA;CB;TB
VA;XA
BA;GA;LA
I have tried with string_to_array, regexp_split_to_array, array_agg, but I don't seem to get close to it.
Thanks.
I hope this would be easy to understand:
WITH tab AS (
SELECT
*
FROM
unnest(ARRAY[
'CA;TB;BA;CB',
'XA;VA',
'GA;BA;LA']) AS txt
)
SELECT
string_agg(val, ';')
FROM (
SELECT
txt,
regexp_split_to_table(txt, ';') AS val
FROM
tab
ORDER BY
val
) AS sub
GROUP BY
txt;
First I split values to rows (regexp_split_to_table) and sort. Then group by original values and join again with string_agg.
Output:
BA;CA;CB;TB
BA;GA;LA
VA;XA
I'm probably overcomplicating it:
t=# with a(c)as (values('CA;TB;BA;CB')
,('XA;VA')
,('GA;BA;LA'))
, w as (select string_agg(v,';') over (partition by c order by v), row_number() over (partition by c),count(1) over(partition by c) from a,unnest(string_to_array(a.c,';')) v)
select * from w where row_number = count;
string_agg | row_number | count
-------------+------------+-------
BA;CA;CB;TB | 4 | 4
BA;GA;LA | 3 | 3
VA;XA | 2 | 2
(3 rows)
and here with a little ugly hack:
with a(c)as (values
('CA;TB;BA;CB')
,('XA;VA')
,('GA;BA;LA'))
select translate(array_agg(v order by v)::text,',{}',';') from a, unnest(string_to_array(a.c,';')) v group by c;
translate
-------------
BA;CA;CB;TB
BA;GA;LA
VA;XA
(3 rows)

Merge text in a single column

I have a table, sample records are shown below -
Name ID C.NO Text
---- ---- ---- ----
ABC A 1 first
ABC A 2 xyz
ABC A 3 AMD
ZSD B 1 hoho
ZSD B 2 hihi
now my output would be like -------
Name ID Text
---- --- ----
ABC A firstxyzAMD
ZSD B hohohihi
kindly help me providing sql statement
In SAP Hana, you would use string_agg():
select name, id, string_agg(text, '')
from t
group by name, id;
The equivalent function in MySQL is group_concat(); in Oracle, listagg().
MySQL:
SELECT
GROUP_CONCAT(`text`, '' SEPARATOR '') AS `newtext`
FROM [table]
GROUP BY `name`;
Well, following query worked in my table (MySQL) and I got the exact result as per your specification
select
Name,
ID,
group_concat(Text SEPARATOR '')
from table_name
group by ID

SQL - Search a table for all instances where a value is repeated

I'm looking to find a way to search a table for duplicate values and return those duplicates (or even just one of the set of duplicates) as the result set.
For instance, let's say I have these data:
uid | semi-unique id
1 | 12345
2 | 21345
3 | 54321
4 | 41235
5 | 12345
6 | 21345
I need to return either:
12345
12345
21345
21345
Or:
12345
21345
I've tried googling around and keep coming up short. Any help please?
To get each row, you can use window functions:
select t.*
from (select t.*, count(*) over (partition by [semi-unique id]) as totcnt
from t
) t
where totcnt > 1
To get just one instance, try this:
select t.*
from (select t.*, count(*) over (partition by [semi-unique id]) as totcnt,
row_number() over (partition by [semi-unique id] order by (select NULL)
) as seqnum
from t
) t
where totcnt > 1 and seqnum = 1
The advantage of this approach is that you get all the columns, instead of just the id (if that helps).
Sorry, I was short on time earlier so I couldn't explain my answer. The first query groups the semi_unique_ids that are the same and only returns the ones that have a duplicate.
SELECT semi_unique_id
FROM your_table
GROUP BY semi_unique_id
HAVING COUNT(semi_unique_id) > 1
If you wanted to get the uid in the query too you can easily add it like so.
SELECT uid,
semi_unique_uid
FROM your_table
GROUP BY
semi_unique_id,
uid
HAVING COUNT(semi_unique_id) > 1
Lastly if you would like to get an idea of how many duplicates per row returned you would do the following.
SELECT uid,
semi_unique_uid,
COUNT(semi_unique_uid) AS unique_id_count
FROM your_table
GROUP BY
semi_unique_id,
uid
HAVING COUNT(semi_unique_id) > 1
SELECT t.semi_unique_id AS i
FROM TABLE t
GROUP BY
t.semi_unique_id
HAVING (COUNT(t.semi_unique_id) > 1)
Try this for sql-server