Union & Pivot multiple tables with different values for similar records - sql

I have multiple tables with the same customer records, but each table has it's own cost currency.
Table 1:
User Country COST_USD
1 USA 10
2 USA 5
3 USA 3
Table 2:
User Country COST_EUR
1 USA 12
2 USA 7
3 USA 5
Table 3:
User Country COST_YEN
1 USA 100
2 USA 50
3 USA 30
What I am looking for is to Union those tables and then pivot the currencies to individual columns (or pivot then union) as follows:
User Country COST_USD COST_EUR COST_YEN
1 USA 10 12 100
2 USA 5 7 50
3 USA 3 3 30
I have tried union all and then pivot but that didn't work since I have differnt currency columns.

Is this what you were thinking of-
select user, country, MAX(COST_USD) COST_USD,MAX(COST_EUR) COST_EUR,MAX(COST_YEN ) COST_YEN
from
(
select user, country, COST_USD, NULL AS COST_EUR, NULL AS COST_YEN from table1
union all
select user, country, NULL AS COST_USD, COST_EUR, NULL AS COST_YEN from table2
union all
select user, country, NULL AS COST_USD, NULL AS COST_EUR, COST_YEN from table3
)T
group by user, country
but if you have many currency columns then you ought to maybe unpivot and union all and the pivot back.
select user, country, currency, amount
from
(select user, country, cost_curr from tableN)U
unpivot
(amount for currency in (COST_EUR, COST_USD,COST_YEN, cost_curr))UPVT
The above is done for all tables and the all resulting unpivots are unioned all and the pivoted back.
As you can see this is quite tedious.
or change you design if possible.

As I mentioned in the comments, this is just a JOIN. Based on your sample data, an INNER JOIN:
SELECT T1.[User], --USER is a reserved keyword, and should not be used for object names
T1.Country,
T1.COST_USD,
T2.COST_EUR,
T3.COST_YEN
FROM dbo.Table1 T1
JOIN dbo.Table2 T2 ON T1.[User] = T2.[User] --USER is a reserved keyword, and should not be used for object names
AND T1.Country = T2.Country
JOIN dbo.Table3 T3 ON T1.[User] = T3.[User] --USER is a reserved keyword, and should not be used for object names
AND T1.Country = T3.Country;
Of course, I doubt all your table have a value for a specific user, so you likely want a FULL OUTER JOIN:
SELECT COALESCE(T1.[User],T2.[User],T3.[User]) AS [User], --USER is a reserved keyword, and should not be used for object names
COALESCE(T1.Country,T2.Country,T3.Country) AS Country,
T1.COST_USD,
T2.COST_EUR,
T3.COST_YEN
FROM dbo.Table1 T1
FULL OUTER JOIN dbo.Table2 T2 ON T1.[User] = T2.[User] --USER is a reserved keyword, and should not be used for object names
AND T1.Country = T2.Country
FULL OUTER JOIN dbo.Table3 T3 ON T3.[User] IN (T1.[User],T2.[User])--USER is a reserved keyword, and should not be used for object names
AND T3.Country IN (T1.Country,T2.Country);
But, like I mentioned, the real solution is fix your design, and have a single table that looks something like this:
CREATE TABLE dbo.YourTable (UserID int,
Country nvarchar(50),
Currency char(3),
Value decimal(12,0));
Then your data would look like this:
INSERT INTO dbo.YourTable
VALUES(1,'USA','USD',10),
(1,'USA','EUR',12),
(1,'USA','YEN',100);
And finally you would get your results with conditional aggregation:
SELECT UserID,
Country,
MAX(CASE Currency WHEN 'USD' THEN Value END) AS COST_USD,
MAX(CASE Currency WHEN 'EUR' THEN Value END) AS COST_EUR,
MAX(CASE Currency WHEN 'YEN' THEN Value END) AS COST_YEN
FROM dbo.YourTable
GROUP BY UserID,
Country;

Related

SQLite query to get table based on values of another table

I am not sure what title has to be here to correctly reflect my question, I can only describe what I want.
There is a table with fields:
id, name, city
There are next rows:
1 John London
2 Mary Paris
3 John Paris
4 Samy London
I want to get a such result:
London Paris
Total 2 2
John 1 1
Mary 0 1
Samy 1 0
So, I need to take all unique values of name and find an appropriate quantity for unique values of another field (city)
Also I want to get a total quantity of each city
Simple way to do it is:
1)Get a list of unique names
SELECT DISTINCT name FROM table
2)Get a list of unique cities
SELECT DISTINCT city FROM table
3)Create a query for every name and city
SELECT COUNT(city) FROM table WHERE name = some_name AND city = some_city
4)Get total:
SELECT COUNT(city) FROM table WHERE name = some_name
(I did't test these queries, so maybe there are some errors here but it's only to show the idea)
As there are 3 names and 2 cities -> 3 * 2 = 6 queries to DB
But for a table with 100 cities and 100 names -> 100 * 100 = 10 000 queries to DB
and it may take a lot of time to do.
Also, names and cities may be changed, so, I can't create a query with predefined names or cities as every day it's new ones, so, instead of London and Paris it may be Moscow, Turin and Berlin. The same thing with names.
How to get such table with one-two queries to original table using sqlite?
(sqlite: I do it for android)
You can get the per-name results with conditional aggregation. As for the total, unfortunately SQLite does not support the with rollup clause, that would generate it automatically.
One workaround is union all and an additional column for ordering:
select name, london, paris
from (
select name, sum(city = 'London') london, sum(city = 'Paris') paris, 1 prio
from mytable
group by name
union all
select 'Total', sum(city = 'London'), sum(city = 'Paris'), 0
from mytable
) t
order by prio, name
Actually the subquery might not be necessary:
select name, sum(city = 'London') london, sum(city = 'Paris') paris, 1 prio
from mytable
group by name
union all
select 'Total', sum(city = 'London'), sum(city = 'Paris'), 0
from mytable
order by prio, name
#GMB gave me the idea of using group by, but as I do it for SQLite on Android, so, the answer looks like:
SELECT name,
COUNT(CASE WHEN city = :london THEN 1 END) as countLondon,
COUNT(CASE WHEN city = :paris THEN 1 END) as countParis
FROM table2 GROUP BY name
where :london and :paris are passed params, and countLondon and countParis are fields of the response class

how to name two columns dependind on another column value for a table in SQL server

I need to name two columns for a table in SQL server.
table1:
id type value
1 th 81648
1 nh 9794
2 nh 7689
2 th 9895
I need to get a table:
id value_th value_nh // the column names depend on type
1 81648 9794
2 9895 7689
How to design the SQL query?
You can do this with pivot. I prefer conditional aggregation:
select t.id,
max(case when type = 'nh' then value end) as value_nh,
max(case when type = 'th' then value end) as value_th
from table t
group by t.id
just use a simple join with nested selects:
select t1.id,t1.value_nh ,t2.value_th from
(select id, value as value_nh where type='nh') t1
join (select id, value as value_th where type='th') t2 on t1.id=t2.id

Union and Aggregation in SQL

I have 3 SQL queries I want to combine them in one table using Union I tried different queries but none of them worked, What am I missing?
SELECT DISTINCT
place.type AS type,
COUNT(place.type)AS place,
0 AS msd,
0 AS county
FROM place
GROUP BY place.type;
SELECT DISTINCT
mcd.type,
0 AS place,
COUNT(mcd.type) AS msd,
0 AS county
FROM mcd
GROUP BY mcd.type;
SELECT DISTINCT
county.type,
0 AS place,
0 AS msd,
COUNT(county.type) AS county
FROM county
GROUP BY county.type;
So the final output would be the scheme (type,place,mcd,county) where type contains all different values for types from the 3 tables and place contains the number times the value of type appears in place table and the same for mcs and county.
You'd need an outer query to get the type values from the three queries combined.
Here's an example, using the UNION ALL set operator to combine the results of the three queries. That query is an inline view, referenced by an outer query that does a GROUP BY on the type column, and SUM aggregate on the COUNT/0 columns.
SELECT t.type
, SUM(t.place)
, SUM(t.msd)
, SUM(t.county)
FROM ( SELECT place.type AS type
, COUNT(place.type) AS place
, 0 AS msd
, 0 AS county
FROM place
GROUP BY place.type
UNION ALL
SELECT mcd.type
, 0 AS place
, COUNT(mcd.type) AS msd
, 0 AS county
FROM mcd
GROUP BY mcd.type
UNION ALL
SELECT county.type
, 0 AS place
, 0 AS msd
, COUNT(county.type) AS county
FROM county
GROUP BY county.type
) t
GROUP BY t.type
With the GROUP BY clause in each query, the DISTINCT keyword isn't needed.
It seems join 3 table together would be more suitable for your requirement.
SELECT coalese(place.type,mcd.type,conty.type) AS type,
COUNT(place.type)AS place,
COUNT(mcd.type) AS msd,
COUNT(county.type) AS county
FROM place
FULL OUTER JOIN mcd ON place.type = mcd.type
FULL OUTER JOIN county ON place.type = county.type
GROUP BY coalese(place.type,mcd.type,conty.type)

Best solution for SQL without looping

I'm relatively new to SQL, and am trying to find the best way to attack this problem.
I am trying to take data from 2 tables and start merging them together to perform analysis on it, but I don't know the best way to go about this without looping or many nested subqueries.
What I've done so far:
I have 2 tables. Table1 has user information and Table2 has information on orders(prices and dates, as well as user)
What I need to do:
I want to have a single row for each user that has a summary of information about all of their orders. I'm looking to find the sum of prices of all orders by each user, the max price paid by that user, and the number of orders. I'm not sure how to best manipulate my data in SQL.
Currently, my code looks as follows:
Select alias1.*, Table2.order_id, Table2.price, Table2.order_date
From (Select * from Table1 where country='United States') as alias1
LEFT JOIN Table2
on alias1.user_id = Table2.user_id
This filters out the datatypes by country, and then joins it with users, creating a record of each order including the user information. I don't know if this is a helpful step, but this is part of my first attempt playing around with the data. I was thinking of looping over this, but I know that is against the spirit of SQL
Edit: Here is an example of what I have and what I want:
Table 1(user info):
user_id user_country
1 United States
2 United Kingdom
(etc)
Table 2(order info):
order_id price user_id
100 5.00 1
101 3.50 2
102 2.50 1
103 1.00 1
104 8.00 2
What I would like output:
user_id user_country total_price max_price number_of_orders
1 United States 8.50 5.00 3
2 United Kingdom 11.50 8.00 2
Here's one way to do this:
SELECT alias1.user_id,
MAX(alias1.user_name) As user_name,
SUM(Table2.price) As UsersTotalPrice,
MAX(Table2.price) As UsersHighestPrice
FROM Table1 As alias1
LEFT JOIN Table2 ON alias1.user_id = Table2.user_id
WHERE country = 'United States'
GROUP BY user_id
If you can give us the actual table definitions, then we can show you some actual working queries.
Something like this? Agregate the rows in table2 and then join to table 1 for the detail info you want?
SELECT Table1.*,agg.thesum FROM
(SELECT UserID, SUM(aggregatedata) as thesum FROM Table2 GROUP BY UserID) agg
INNER JOIN Table1 on table1.userid = agg.userid
This should work
select table1.*, t2.total_price, t2.max_price, t2.order_count
from table1
join (selectt user_id, sum(table2.price) as total_price, max(table2.price) as max_price, count(order_id) as order_count from table2 as t2 group by t2.user_id)
on table1.user_id = t2.user_id
where t1.country = 'untied_states'
EDIT: (removed:"dont use explicit join" this was wrong, I meant:)
Try to use the following Sytax, for better understanding what goes on:
1st step:
select
user.user_id, -- < you must tell the DB userid of which column
user_country,
price,
price
from -- now just the two tables:
Table1 as user, --table1 is a bad name, we use 'user'
Table2 as order
where user.user_id = order.user_id
so you will get somthing like:
user_id user_country price price
1 alabama 5 5
2 nebrasca 1 1
2 alabama 7 7
1 alabama 7 7
2 alabama 3 7
and so on ..
the next step is to add an other where usercountry='alabama' so 'nebrasca' is off
user_id user_country price price
1 alabama 5 5
2 alabama 7 7
1 alabama 7 7
2 alabama 3 7
now you are ready for "aggregate": just select the MAX and SUM of price, but you have to tell the SQL engine what columes are 'fixed' = group by
select
user.user_id, user_country, MAX(price), SUM(price)
from
Table1 as user,
Table2 as order
where user.user_id = order.user_id
and user_country='alabama'
group by user_id, user_country

Crosstab/Pivot query in TSQL on nvarchar columns

I have a Table1:
ID Property
1 Name
2 City
3 Designation
and Table2:
ID RecordID Table1ID Value
1 1 1 David
2 1 2 Tokyo
3 2 1 Scott
4 2 3 Manager
The Table1ID of Table2 maps to Table1's ID. Now I wish to show the Table1 Property column values as column headers and have a result set in format like:
RecordID Name City Designation
1 David Tokyo NULL
2 Scott NULL Manager
What is the best/efficient way to achieve this in T-SQL considering that the number of records in Table1 (i.e. the columns in result set) can change and thus should be handled dynamically.
Although I tried PIVOT and CASE based queries, but have been struggling with both of them. :(
Any help/guidance would be appreciated.
Thanks!
Update:
I've been able to create the dynamic query, but one thing which I am still not able to understand is why MAX has been used in the CASE statements. Kindly ignore my noobness.
Use:
SELECT t2.recordid,
MAX(CASE WHEN t1.property = 'Name' THEN t2.value END) AS name,
MAX(CASE WHEN t1.property = 'City' THEN t2.value END) AS city,
MAX(CASE WHEN t1.property = 'Designation' THEN t2.value END) AS designation
FROM TABLE2 t2
JOIN TABLE1 t1 ON t1.id = t2.table1id
GROUP BY t2.recordid
ORDER BY t2.recordid