How to get the column name of a value in PostgreSQL? - sql

Suppose we have 3 tables:
Table1:
ID FrenchCity
1 Paris
2 Lille
3 Lyon
Table2:
ID IntlCity
1 Lille
2 Geneva
3 Toulouse
Table3:
ID BritishCity
1 London
2 Leeds
I would like to get the column name correspondent with a value.
For instance, I give a value Lille and SQL should return Table1.FrenchCity Table2.IntlCity.
As I said, I would like to get the column name of a value. So Lille exists in 2 tables, I would like SQL to return the {{table name}}.{{column name}}
How to write a query to do that?

This work for you ?
SELECT 'Table1.FrenchCity' as fieldName
FROM Table1
WHERE FrenchCity = 'Lille'
UNION ALL
SELECT 'Table2.IntlCity' as fieldName
FROM Table2
WHERE IntlCity = 'Lille'
UNION ALL
SELECT 'Table3.BritishCity' as fieldName
FROM Table3
WHERE BritishCity = 'Lille'
Then you can use array_agg
SELECT array_agg(fieldName)
FROM (
previous union query
) t

you better create one table with 3 columns:
ID COUNTRY fieldName CITY
1 France FrenchCity Paris
2 France FrenchCity Lille
3 France FrenchCity Lyon
4 Intl IntlCity Lille
5 Intl IntlCity Geneva
6 Intl IntlCity Toulouse
ect.
then use query:
SELECT country || '.' || fieldName
FROM three_col_table
WHERE CITY = 'Lille'

If you don't wont to use DB metadata then you can to convert table data into the series of (column_name, column_value) pairs using row_to_json and json_each_text functions:
with
-- Demo data start
Table1(ID, FrenchCity) as (values
(1, 'Paris'),
(2, 'Lille'),
(3, 'Lyon')),
Table2(ID, IntlCity) as (values
(1, 'Lille'),
(2, 'Geneva'),
(3, 'Toulouse')),
-- Demo data end
my_data as (
select 'Table1' as tbl, j.*
from Table1 as t, json_each_text(row_to_json(t.*)) as j(fld,val)
union all
select 'Table2' as tbl, j.*
from Table2 as t, json_each_text(row_to_json(t.*)) as j(fld,val)
-- ... and so on ...
)
select *, format('%s.%s', tbl, fld) as desirede_value from my_data
where val ilike 'lille';
tbl | fld | val | desirede_value
--------+------------+-------+-------------------
Table1 | frenchcity | Lille | Table1.frenchcity
Table2 | intlcity | Lille | Table2.intlcity
(2 rows)

Related

Creating a query that creates a column from two columns

I'm sorry if the title is not descriptive enough, but I really didn't know how to put it in a sentence. Lets say I have this table:
ID | LOC | NAMEA | NAMEB
------------------------
0 | BL | X | Y
1 | BG | Z | NULL
I want to know if it is possible to write a query that returns this from the table:
ID | LOC | NAME
------------------------
0 | BL | X
0 | BL | Y
1 | BG | Z
I know that if I need to use this, the database is bad and I do not plan on doing this, but I just want to know if it is possible and how.
You can use this-
SELECT ID, LOC, NAMEA AS NAME
FROM your_table
WHERE NAMEA IS NOT NULL
UNION ALL
SELECT ID, LOC, NAMEB AS NAME
FROM your_table
WHERE NAMEB IS NOT NULL
UNION (ALL) helps:
SQL> with test (id, loc, namea, nameb) as
2 (select 0, 'BL', 'X', 'Y' from dual union all
3 select 1, 'BG', 'Z', NULL from dual
4 )
5 select id, loc, namea from test where namea is not null
6 union all
7 select id, loc, nameb from test where nameb is not null
8 order by id;
ID LO N
---------- -- -
0 BL X
0 BL Y
1 BG Z
SQL>
Use union all
select id, loc,nameA from tablename where nameA is not null
union all
select id, loc,nameB from tablename where nameB is not null
You want union all :
select t.*
from (select t.id, t.loc, t.namea as name
from table t
union all
select t.id, t.loc, t.nameb
from table t
) t
where name is not null
order by id;
I don't have a direct query to return the output mentioned above but you can try the following steps. By doing this you will create a new table with the data you require as an output.
step 1:
`create table test2 as (select id,loc,namea from test1);`
step 2:create table test3 as (select id,loc,nameb from test1);
step 3:insert into test2(id,loc,namea) select * from test3 where nameb='y';
step 4: select * from test2
One more thing namea,nameb should have same data size.

SELECT subquery with 2 return values

I want to select multiple columns from a subquery. Here my minimal example:
A function that returns two values:
CREATE OR REPLACE FUNCTION dummy_function(my_text text)
RETURNS TABLE (id Integer, remark text) AS $$
BEGIN
RETURN QUERY SELECT 42, upper(my_text);
END;
$$ LANGUAGE plpgsql;
My not working query:
SELECT
id,
city_name,
dummy_function(city_name)
FROM
(SELECT 1 as id, 'Paris' as city_name
UNION ALL
SELECT 2 as id, 'Barcelona' as city_name
) AS dummy_table
My wrong result:
id | city_name | dummy_function
----+-----------+----------------
1 | Paris | (42,PARIS)
2 | Barcelona | (42,BARCELONA)
But I would like to have a result like this:
id | city_name | number | new_text
----+-----------+---------------------
1 | Paris | 42 | PARIS
2 | Barcelona | 42 | BARCELONA
Do you know how to achieve this without running the function twice?
Use the function returning row (or set of rows) in the FROM clause:
SELECT
dummy_table.id,
city_name,
dummy_function.id,
remark
FROM
(SELECT 1 as id, 'Paris' as city_name
UNION ALL
SELECT 2 as id, 'Barcelona' as city_name
) AS dummy_table,
LATERAL dummy_function(city_name)
id | city_name | id | remark
----+-----------+----+-----------
1 | Paris | 42 | PARIS
2 | Barcelona | 42 | BARCELONA
(2 rows)
Per the documentation:
Table functions appearing in FROM can also be preceded by the key word LATERAL, but for functions the key word is optional; the function's arguments can contain references to columns provided by preceding FROM items in any case.
SELECT
dummy_table.id,
city_name,
df.id as number,
df.remark as new_text
FROM
(SELECT 1 as id, 'Paris' as city_name
UNION ALL
SELECT 2 as id, 'Barcelona' as city_name
) AS dummy_table,
dummy_function(city_name) df

Get unique comma separated values using group by in SQL

My table is as below
ID | Name | City
------ | ------ | ------
1 | ABC | London, Paris
1 | ABC | Paris
1 | ABC | Japan
2 | XYZ | Delhi
2 | XYZ | Delhi, New York
My output needs to be like this:
ID | Name | City
------ | ------ | ------
1 | ABC | London, Paris, Japan
2 | XYZ | Delhi, New York
I see it as a 2 step process:
Concatenate all the unique cities for each ID and Name. Example: For ID 1 and Name ABC Cities would be London, Paris, Japan
Update the concatenated string by grouping the ID and Name.
I am able to do this for just one group, but how do I do this for all the different groups in the table?
Also, would cursors come into picture here when I want to update the string to all the rows matching the ID and Name.
Any help or idea on this would be appreciated.
You should consider normalizing your table first.
Here, you first want to convert all the comma separated values into separate rows and then, group them together using STUFF and FOR XML PATH.
with your_table (ID, name, City)
as (
select 1, 'ABC', 'London, Paris'
union all
select 1, 'ABC', 'Paris'
union all
select 1, 'ABC', 'Japan'
union all
select 2, 'XYZ', 'Delhi'
union all
select 2, 'XYZ', 'Delhi, New York'
), your_table_modified
as (
select distinct id, name, Split.a.value('.', 'varchar(100)') City
from (
select id, name, cast('<x>' + replace(City, ', ', '</x><x>') + '</x>' as xml) as x
from your_table
) t
cross apply x.nodes('/x') as Split(a)
)
select id, name, stuff((
select ', ' + city
from your_table_modified t2
where t.id = t2.id
for xml path(''), type
).value('(./text())[1]', 'varchar(max)')
, 1, 2, '')
from your_table_modified t
group by id, name;
Produces:
Try this:
select y.id,y.name,y.city from (
select id,name, case when Id=1 and name='ABC' then 'London,Paris,Japan'
when Id=2 and name='XYZ' then 'Delhi,Newyork'
end as CIty
,row_number () over (partition by id order by name asc) as rnk
from Yourtable
)y
where y.rnk=1

SQL statement to conditionally selecting records based on the previous record

I have 2 tables as below
Table 1 : Animal (ID is a primary key)
ID |Animal
----------
1 |Dog
2 |Cat
3 |Fish
4 |Bird
5 |Elephant
Table 2: Pet (ID here is foreign keys to the Animal table)
ID | Animal | Name
----------
1 | Dog | Annie
1 | Dog | Buckie
2 | Cat | Conner
2 | Cat | Kitten
3 | Fish| Lala
I want to write a SQL statement to append a row with "Fish" right after wherever a specific pet "Dog" appears without breaking the order.
Expected result should be:
ID | Animal | Name
----------
1 | Dog | Annie
3 | Fish| NULL
1 | Dog | Buckie
3 | Fish| NULL
2 | Cat | Conner
2 | Cat | Kitten
3 | Fish| Lala
I'm not too sure about Oracle11g but I think it has ROW_NUMBER.
You could add a row number to the original table,
and then union a fish table with corresponding row numbers.
For example
WITH Tablex AS (
SELECT ROW_NUMBER() OVER(ORDER BY ID, Name) AS ref_id, *
FROM your_table
)
SELECT ID, Animal, Name
FROM (SELECT *
FROM Tablex
UNION ALL
SELECT *
FROM
(SELECT ref_id, 3 AS ID, 'Fish' AS Animal, NULL AS Name
FROM TableX
WHERE Animal = 'Dog'
) x
) X
ORDER BY ref_id, id
As commented above, the order of rows depends only on the ORDER BY clause and the order may not be actually incorporated in the table (if you put it INTO something).
Try this
select tn.Id
, case when tt.rn = 0 then tn.Animal else 'Fish' end Animal
, case when tt.rn = 0 then tn.Name else NULL end Name
, tn.rn+tt.rn rn
from (
select ID, Animal, Name, 2 * row_number() over (order by id, name) as rn
from pet
) tn
join (
select 0 rn from dual union
select 1 from dual
) tt on tt.rn <= case Animal when 'Dog' then 1 else 0 end
order by tn.rn+tt.rn;
with Q as (
select ID, Animal, Name,
row_number() over (order by id, name) rnum
from Pet
)
select ID, Animal, Name, rnum
from Q
union all
select 3, 'Fish', NULL, rnum+0.5
from Q
where ID=1 and name in('Annie','Buckie')
order by rnum

Sql select query based on a column value

I have a Table1 like this:
ApplicableTo IdApplicable
---------------------------
Dept 1
Grade 3
section 1
Designation 2
There other tables like:
tblDept:
ID Name
1 dept1
2 baking
3 other
tblGrade:
ID Name
1 Grd1
2 Manager
3 gr3
tblSection:
id Name
1 Sec1
2 sec2
3 sec3
tblDesignation:
id Name
1 Executive
2 Developer
3 desig3
What I need is a query for table1 in such a way that gives me
ApplicableTo (table1)
Name (from the relevant table based on the value in `ApplicableTo` column)
Is this possible?
Desired Result:
eg: ApplicableTo IdApplicable Name
Dept 1 dept1
grade 3 gr3
Section 1 sec1
Designation 2 Developer.
This is the result I desire.
You could do something like the following so the applicable to becomes part of the JOIN predicate:
SELECT t1.ApplicableTo, t1.IdApplicable, n.Name
FROM Table1 AS t1
INNER JOIN
( SELECT ID, Name, 'Dept' AS ApplicableTo
FROM tblDept
UNION ALL
SELECT ID, Name, 'Grade' AS ApplicableTo
FROM tblGrade
UNION ALL
SELECT ID, Name, 'section' AS ApplicableTo
FROM tblSection
UNION ALL
SELECT ID, Name, 'Designation' AS ApplicableTo
FROM tblDesignation
) AS n
ON n.ID = t1.IdApplicable
AND n.ApplicableTo = t1.ApplicableTo
I would generally advise against this approach, although it may seem like a more consice approach, you would be better having 4 separate nullable columns in your table:
ApplicableTo | IdDept | IdGrade | IdSection | IdDesignation
-------------+--------+---------+-----------+---------------
Dept | 1 | NULL | NULL | NULL
Grade | NULL | 3 | NULL | NULL
section | NULL | NULL | 1 | NULL
Designation | NULL | NULL | NULL | 2
This allows you to use foreign keys to manage your referential integrity properly.
You can use CASE here,
SELECT ApplicableTo,
IdApplicable,
CASE
WHEN ApplicableTo = 'Dept' THEN (SELECT Name FROM tblDept WHERE tblDept.ID = IdApplicable)
WHEN ApplicableTo = 'Grade' THEN (SELECT Name FROM tblGrade WHERE tblGrade.ID = IdApplicable)
WHEN ApplicableTo = 'Section' THEN (SELECT Name FROM tblSection WHERE tblSection.ID = IdApplicable)
WHEN ApplicableTo = 'Designation' THEN (SELECT Name FROM tblDesignation WHERE tblDesignation.ID = IdApplicable)
END AS 'Name'
FROM Table1
The easiest way to achieve that is to add an extra column in table1 to keep the table where id is refferred to. Otherwise you can't know in which table the applicable id is reffered to.
Or you can create the applicable id in a way that you can extract the table afterwords from it for example a1 for id 1 in tblDept. And then use [case] (http://dev.mysql.com/doc/refman/5.0/en/case.html) (for mysql) in order to make the correct Join.