Split data over multiple columns into rows (in SQL or SAS) - sql

I have data in the following form:
Country City1 City2 City3 AverageTemperature
UK London Glasgow Manchester 15
Italy Rome Naples Venice 25
Germany Munich Berlin 20
USA New York 25
With either SQL or a SAS data step, I would like to get the data in this form:
Country City AverageTemperature
UK London 15
UK Glasgow 15
UK Manchester 15
Italy Rome 25
Italy Naples 25
Italy Venice 25
Germany Munich 20
Germany Berlin 20
USA New York 25
So that I have the data across individual rows. I have thought about doing this by looping over the three city columns where the city is not blank, but I'm not sure how to confidently do this - is it easily done with either SQL or SAS? Just a pointer would be greatly appreciated.

SELECT COUNTRY, City1, AverageTemperature FROM Table_Name
UNION ALL
SELECT COUNTRY, City2, AverageTemperature FROM Table_Name
UNION ALL
SELECT COUNTRY, City3, AverageTemperature FROM Table_Name
To get rows where City column is not null you can do something like this
SELECT COUNTRY, City1, AverageTemperature FROM Table_Name
WHERE City1 IS NOT NULL
UNION ALL
SELECT COUNTRY, City2, AverageTemperature FROM Table_Name
WHERE City2 IS NOT NULL
UNION ALL
SELECT COUNTRY, City3, AverageTemperature FROM Table_Name
WHERE City3 IS NOT NULL

Simple in a SAS Data Step
data out;
set in;
array cities[3] city1-city3;
format city $12.;
do i=1 to 3;
if compress(cities[i]) ^= "" then do;
city = cities[i];
output;
end;
end;
keep country city AverageTemperature;
run;

You can unpivot this using "relatively" standard SQL. Here is an approach that only requires scanning the data once:
select country, city, averagetemperatur
from (select t.country,
(case when n = 1 then t.city1
when n = 2 then t.city2
when n = 3 then t.city3
end) as city,
t.averagetemperature
from t cross join
(select 1 as n union all select 2 union all select 3) n
) t
where city is not null;
The exact syntax for creating the table with three rows (n) can vary depending on the database.

A macro loop should do the job:
%MACRO Cities ;
%DO N=1 %TO 3 ;
proc sql ;
create table Cities_&N as
select Country, City&N as City, AverageTemperature
from your_table_name_here
where City&N is not null ;
quit ;
%END ;
data Cities ;
set Cities_: ;
run ;
%MEND ;
%Cities ;

Related

SQL How to access element of a different table?

I have this table [pets]
Animal
prev_store
curr_store
Cat
ABC
DEF
Dog
ABC
GHI
Fish
DEF
XYZ
Snake
XYZ
JKM
I also have this other table [pet_store]
Store
Country
ABC
England
DEF
Denmark
GHI
England
XYZ
Denmark
JKM
Denmark
I want to check for each animal in pets table, whether the prev_store and curr_store is in the same Country, and if so, make a record of the Country.
Country
Occurrences
England
1
Denmark
2
SELECT pet_store.Country, count(pet_store.Country)
FROM pet_store, pets
WHERE pets.prev_store = pet_store.Store
and pets.curr_store = pet_store.Store
GROUP BY pet_store.Country
Unsure about how I would then select the animals's Country in relevance to the prev and curr store.
You will have to use pets_store twice, in two roles, in the query:
WITH
-- your data, don't use in query ..
pets(Animal,prev_store,curr_store) AS (
SELECT 'Cat' ,'ABC','DEF'
UNION ALL SELECT 'Dog' ,'ABC','GHI'
UNION ALL SELECT 'Fish' ,'DEF','XYZ'
UNION ALL SELECT 'Snake','XYZ','JKM'
)
,
pets_store(Store,Country) AS (
SELECT 'ABC','England'
UNION ALL SELECT 'DEF','Denmark'
UNION ALL SELECT 'GHI','England'
UNION ALL SELECT 'XYZ','Denmark'
UNION ALL SELECT 'JKM','Denmark'
)
-- real query starts here ...
SELECT
prev_stores.country AS country
, COUNT(*) AS occurrences
FROM pets
JOIN pets_store prev_stores ON prev_store = prev_stores.store
JOIN pets_store curr_stores ON curr_store = curr_stores.store
WHERE prev_stores.country = curr_stores.country
GROUP BY prev_stores.country;
-- out country | occurrences
-- out ---------+-------------
-- out Denmark | 2
-- out England | 1

Select query to return no records

I have Table 1 : EMP as below
ID NAME CITY AMT
-------------------------------------------
1 sajani Bangalore 20
2 Prashanth Bangalore 10
3 Jayvin Bangalore 10
Table 2: EMP1
ID NAME1 CITY1 AMT1
---------------------------------------------
1 Sajani Bangalore 10
1 Sajani Bangalore 10
2 Prashanth Bangalore 10
3 Jayvin Bangalore 10
ID is the Key and is common in both the files. I want a Select SQL statement which states table 1 = table 2 and once select query is executed, it should return 0 records.
Use MINUS operator
SELECT ID, NAME, CITY, AMT
FROM EMP
MINUS
SELECT ID, NAME1, CITY1, SUM(AMT1)
FROM EMP1
GROUP BY ID, NAME1, CITY1
Thanks, I figured out the solution
Select * from emp
where amt <>(
select sum(e1.amt1)
from emp1 e1
where e1.id =emp.id
)

How to search a string inside a string in Oracle

I have a table A with columns Country ID ,Country. and data is like this
US United States
IN India
JP Japan
NP Nepal
etc .
I have different table B which has a column Country which mostly has free text data . Data is like
Texas United States
India KA
XYS Japan WYS
EverestNepal
XYZ
etc.
What i want is that if country column in Table B has a country matching from any column of country in Table A , it should return Country from Table A.
So for the example i gave
Table B has Texas United States --- there is a match in Table A with 'United States' : It should print United States
Table B has India KA ---- there is a match in Table A with 'India':it should print India
EverestNepal --- there is a match in Table A with 'Nepal': it should print Nepal
Table B has
and so on.
To summarize : If the exact match of country in Table B from anywhere in the string is found in Table A, it should print country from Table A
I dont think Like , IN , Substring will work in such situation
INSTR (line #17) is what you probably need.
SQL> with
2 ta (country_id, country) as
3 (select 'US', 'United States' from dual union all
4 select 'IN', 'India' from dual union all
5 select 'JP', 'Japan' from dual union all
6 select 'NP', 'Nepal' from dual
7 ),
8 tb (country) as
9 (select 'Texas United States' from dual union all
10 select 'India KA' from dual union all
11 select 'XYS Japan WYS' from dual union all
12 select 'EverestNepal' from dual union all
13 select 'XYZ' from dual
14 )
15 select b.country B_country,
16 a.country A_country
17 from ta a right join tb b on instr(b.country, a.country) > 0;
B_COUNTRY A_COUNTRY
------------------- -------------
Texas United States United States
India KA India
XYS Japan WYS Japan
EverestNepal Nepal
XYZ
SQL>
You can use like operator to join between the tables:
SELECT
A.COUNTRY
FROM
TABLE_A A
JOIN TABLE_B B ON ( A.COUNTRY LIKE '%'
|| B.COUNTRY
|| '%' );
Cheers!!

Ordering Query for Country & City data for the scenario given

My input data is below :
**Country city**
Australia Sydney
Australia melbourne
India Delhi
India Chennai
India Bangalore
Afghanistan Kabul
Output expected is:
Afghanistan
Kabul
Australia
melbourne
syndey
India
Bangalore
Chennai
Delhi
The data in both columns should be arranged alphabetically(both city level and country level) and result should be single column with above values. The country should be alphabetically ordered and the corresponding cities should go below them which should also be alphabetically ordered.
How can this be done without using an intermediate table in a single query?
You need a UNION ALL query to get one row per country and one row per city in your result:
select coalesce(city, country) as location
from
(
select distinct country, null as city from mytable
union all
select country, city from mytable
)
order by country, city nulls first;
This has a single table scan and also does not need to use UNION to get distinct results:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE cities ( Country, city ) AS
SELECT 'Australia', 'Sydney' FROM DUAL UNION ALL
SELECT 'Australia', 'melbourne' FROM DUAL UNION ALL
SELECT 'India', 'Delhi' FROM DUAL UNION ALL
SELECT 'India', 'Chennai' FROM DUAL UNION ALL
SELECT 'India', 'Bangalore' FROM DUAL UNION ALL
SELECT 'Afghanistan', 'Kabul' FROM DUAL;
Query 1:
SELECT value
FROM (
SELECT c.*,
country AS ctry,
ROW_NUMBER() OVER ( PARTITION BY Country ORDER BY city ) AS rn
FROM cities c
)
UNPIVOT( value FOR key IN ( Country AS 1, City AS 2 ) )
WHERE rn = 1 OR key = 2
ORDER BY ctry, rn, key
Results:
| VALUE |
|-------------|
| Afghanistan |
| Kabul |
| Australia |
| Sydney |
| melbourne |
| India |
| Bangalore |
| Chennai |
| Delhi |

Check for an entry in SQL Server

-----------------------
country | city | ids
-----------------------
India Mumbai 1
India Chennai 2
India Kolkata 3
---------------------
USA New York 2
USA Utah 3
---------------------
I have given a sample from a table. From the table, I am trying to query all the countries without id 1. I wrote this(Country was not included in the Where condition since it needs to apply to all the countries of the table).
Select * from Countries
WHERE id<>1
I got this.
-----------------------
country | city | ids
-----------------------
India Chennai 2
India Kolkata 3
---------------------
USA New York 2
USA Utah 3
---------------------
But I need the output to contain only USA(which does not have id=1). Is there any workaround for this?
SELECT * from Countries WHERE country not in
(SELECT country from Countries WHERE id=1)
use NOT EXISTS
Select *
from Countries c
where not exists
(
select *
from Countries x
where x.country = c.country
and x.id = 1
)
You need to group by Country like below :
SELECT C.Country
FROM City
WHERE C.Country NOT IN
(SELECT country FROM City WHERE id=1)
SQL Fiddle Demo
OR
SELECT
C.Country
FROM
City C
GROUP BY C.Country
HAVING C.Country NOT IN
(
SELECT Country FROM City WHERE Id =1
)
SQL Fiddle Demo