sql convert rows into columns in sql server 2008r2 - sql

I have a tables called customer, hobby's.
A customer has several hobby's. So if I join my table customer with table hobby I get a result like:
[CustomerName] [HobbyName]
Harry Tennis
Harry Football
What I would like is to see a result like this:
[CustomerName] [HobbyName1] [HobbyName2]
Harry Tennis Football
My current query looks like this:
Select tCustomer.name, tHobby.name
from dbo.customer tCustomer
inner join dbo.hobby tHobby on tHobby.customerid = tCustomer.id

You didn't provide many details on your table structure but you can use PIVOT to get the final result, but in order to use this you will first want to use row_number() to generate a unique sequence for each hobby per customer:
select customer, Hobby1, Hobby2
from
(
Select c.name customer,
h.name hobby,
'Hobby'+
cast(row_number() over(partition by c.id
order by h.name) as varchar(10)) seq
from dbo.customer c
inner join dbo.hobby h
on h.customerid = c.id
) d
pivot
(
max(hobby)
for seq in (hobby1, Hobby2)
) piv;
See SQL Fiddle with Demo. If you don't want to use the PIVOT function, then you could also use a CASE expression and an aggregate function:
select customer,
max(case when seq = 1 then hobby end) hobby1,
max(case when seq = 2 then hobby end) hobby2
from
(
Select c.name customer,
h.name hobby,
row_number() over(partition by c.id
order by h.name) seq
from dbo.customer c
inner join dbo.hobby h
on h.customerid = c.id
) d
group by customer;
See SQL Fiddle with Demo

Related

Table Joining Issues

I have difficulties joining the tables below for the desired query as stated.
Theatre (Theatre#, Name, Address, MainTel);
Production (P#, Title, ProductionDirector, PlayAuthor);
Performance (Per#, P#, Theatre#, pDate, pHour, pMinute, Comments);
Client (Client#, Title, Name, Street, Town, County, telNo, e-mail);
Ticket Purchase (Purchase#, Client#, Per#, PaymentMethod, DeliveryMethod, TotalAmount)
Required Query
The theater name (for each theater) and the names of clients who have the highest spending in that theater
SELECT T.NAME, C.NAME, SUM(TOTALAMOUNT)
FROM TICKETPURCHASE TP,
THEATRE T,
CLIENT C,
PERFORMANCE PER
WHERE TOTALAMOUNT = (SELECT MAX (TOTALAMOUNT)
FROM TICKETPURCHASE TP2,
THEATRE T2,
PERFORMANCE PER2,
CLIENT C2,
PRODUCTION P2
WHERE T2.NAME = T.NAME
AND T2.THEATRE# = PER2.THEATRE#
AND TP2.CLIENT# = C2.CLIENT#
AND TP2.PER# =PER2.PER#
AND PER2.P# = P2.P# )
AND C.CLIENT# = TP.CLIENT#
AND T.THEATRE# = PER.THEATRE#
AND TP.PER# = PER.PER#
AND PER.P# = P.P#
GROUP BY T.NAME, C.NAME, TOTALAMOUNT
From Oracle 12, you can do it all in a single query if you aggregate by the primary keys for the theatre and the client to get the total spent and then rank the spending in the ORDER BY clause and use the row limiting clause to get the first ranked values:
SELECT MAX(T.NAME) AS theatre_name,
MAX(C.NAME) AS client_name,
SUM(TP.TOTALAMOUNT) AS amount_spent
FROM TICKETPURCHASE TP
INNER JOIN PERFORMANCE PER
ON (TP.PER# = PER.PER#)
INNER JOIN THEATRE T
ON (T.THEATRE# = PER.THEATRE#)
INNER JOIN CLIENT C
ON (C.CLIENT# = TP.CLIENT#)
GROUP BY
T.THEATRE#,
C.CLIENT#
ORDER BY
DENSE_RANK() OVER (
PARTITION BY T.THEATRE#
ORDER BY SUM(TP.TOTALAMOUNT) DESC
)
FETCH FIRST ROW WITH TIES;
In earlier versions, you can use the same technique and filter in an outer query:
SELECT theatre_name,
client_name,
amount_spent
FROM (
SELECT MAX(T.NAME) AS theatre_name,
MAX(C.NAME) AS client_name,
SUM(TP.TOTALAMOUNT) AS amount_spent,
DENSE_RANK() OVER (
PARTITION BY T.THEATRE#
ORDER BY SUM(TP.TOTALAMOUNT) DESC
) As rnk
FROM TICKETPURCHASE TP
INNER JOIN PERFORMANCE PER
ON (TP.PER# = PER.PER#)
INNER JOIN THEATRE T
ON (T.THEATRE# = PER.THEATRE#)
INNER JOIN CLIENT C
ON (C.CLIENT# = TP.CLIENT#)
GROUP BY
T.THEATRE#,
C.CLIENT#
)
WHERE rnk = 1;

How optimize select with max subquery on the same table?

We have many old selects like this:
SELECT
tm."ID",tm."R_PERSONES",tm."R_DATASOURCE", ,tm."MATCHCODE",
d.NAME AS DATASOURCE,
p.PDID
FROM TABLE_MAPPINGS tm,
PERSONES p,
DATASOURCES d,
(select ID
from TABLE_MAPPINGS
where (R_PERSONES, MATCHCODE)
in (select
R_PERSONES, MATCHCODE
from TABLE_MAPPINGS
where
id in (select max(id)
from TABLE_MAPPINGS
group by MATCHCODE)
)
) tm2
WHERE tm.R_PERSONES = p.ID
AND tm.R_DATASOURCE=d.ID
and tm2.id = tm.id;
These are large tables, and queries take a long time.
How to rebuild them?
Thank you
You can query the table only once using something like (untested as you have not provided a minimal example of your create table statements or sample data):
SELECT *
FROM (
SELECT m.*,
COUNT(CASE WHEN rnk = 1 THEN 1 END)
OVER (PARTITION BY r_persones, matchcode) AS has_max_id
FROM (
SELECT tm.ID,
tm.R_PERSONES,
tm.R_DATASOURCE,
tm.MATCHCODE,
d.NAME AS DATASOURCE,
p.PDID,
RANK() OVER (PARTITION BY tm.matchcode ORDER BY tm.id DESC) As rnk
FROM TABLE_MAPPINGS tm
INNER JOIN PERSONES p ON tm.R_PERSONES = p.ID
INNER JOIN DATASOURCES d ON tm.R_DATASOURCE = d.ID
) m
)
WHERE has_max_id > 0;
First finding the maximum ID using the RANK analytic function and then finding all the relevant r_persones, matchcode pairs using conditional aggregation in a COUNT analytic function.
Note: you want to use the RANK or DENSE_RANK analytic functions to match the maximums as it can match multiple rows per partition; whereas ROW_NUMBER will only ever put a single row per partition first.
You're querying table_mappings 3 times; how about doing it only once?
WITH
tab_map
AS
(SELECT a.id,
a.r_persones,
a.matchcode,
a.datasource,
ROW_NUMBER ()
OVER (PARTITION BY a.matchcode ORDER BY a.id DESC) rn
FROM table_mappings a)
SELECT tm.id,
tm.r_persones,
tm.matchcode,
d.name AS datasource,
p.pdid
FROM tab_map tm
JOIN persones p ON p.id = tm.r_persones
JOIN datasources d ON d.id = tm.r_datasource
WHERE tm.rn = 1

Selecting rows with the most repeated values at specific column

Problem in general words: I need to select value from one table referenced to the most repeated values in another table.
Tables have this structure:
screenshot
screenshot2
The question is to find country which has the most results from sportsmen related to it.
First, INNER JOIN tables to have relation between result and country
SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id);
Then, I count how much time each country appear
SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id))
GROUP BY country
;
And got this screenshot3
Now it feels like I'm one step away from solution ))
I guess it's possible with one more SELECT FROM (SELECT ...) and MAX() but I can't wrap it up?
ps:
I did it with doubling the query like this but I feel like it's so inefficient if there are millions of rows.
SELECT country
FROM (SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id)
) GROUP BY country
)
WHERE highest_participation = (SELECT MAX(highest_participation)
FROM (SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id)
) GROUP BY country
))
Also I did it with a view
CREATE VIEW temp AS
SELECT country as country_with_most_participations, COUNT(country) as country_participate_in_#_comp
FROM(
SELECT country, competition_id FROM result
INNER JOIN sportsman USING(sportsman_id)
)
GROUP BY country;
SELECT country_with_most_participations FROM temp
WHERE country_participate_in_#_comp = (SELECT MAX(country_participate_in_#_comp) FROM temp);
But not sure if it's easiest way.
If I understand this correctly you want to rank the countries per competition count and show the highest ranking country (or countries) with their count. I suggest you use RANK for the ranking.
select country, competition_count
from
(
select
s.country,
count(*) as competition_count,
rank() over (order by count(*) desc) as rn
from sportsman s
inner join result r using (sportsman_id)
group by s.country
) ranked_by_count
where rn = 1
order by country;
If the order of the result rows doesn't matter, you can shorten this to:
select s.country, count(*) as competition_count
from sportsman s
inner join result r using (sportsman_id)
group by s.country
order by count(*) desc
fetch first rows with ties;
You seem to be overcomplicating this. Starting from your existing join query, you can aggregate, order the results and keep the top row(s) only.
select s.country, count(*) cnt
from sportsman s
inner join result r using (sportsman_id)
group by s.country
order by cnt desc
fetch first 1 row with ties
Note that this allows top ties, if any.
SELECT country
FROM (SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id)
) GROUP BY country
order by 2 desc
)
where rownum=1

How can I randomly distribute rows in one table to rows in another table in oracle SQL

I am trying to figure out a SQL query that will distribute records from one table to another table randomly.
for example :
I have a table of Customers, and I want to assign each a car out of a table of cars.
I want to make sure that the car are randomly distributed, but there is no property of an Customers that would predict which car they would receive.
Customers:
(Jon,Sam,Sara,Jack,Adam,Adrian)
Cars:
(BMW,Dodge,Lexus)
Result:
(Jon-BMW,Sam-Lexus,Sara-BMW,Jack-Dodge,Adam-Dodge,Adrian-BMW)
How can i do that in Oracle SQL?
Here's one option:
SQL> with t as
2 (select u.name ||'-'||a.name comb,
3 row_number() over (partition by u.name order by dbms_random.value(1, n.cnt)) rn
4 from customers u cross join cars a
5 join (select count(*) cnt from cars) n on 1 = 1
6 )
7 select t.comb
8 from t
9 where rn = 1;
COMB
-----------------------------------------
Adam-Lexus
Adrian-BMW
Jack-Lexus
Jon-BMW
Sam-Dodge
Sara-Lexus
6 rows selected.
SQL>
One method that might be more efficient than a full cross join is:
select c.*, cc.car
from (select c.*,
row_number() over (order by dbms_random.value(1, cc.cnt) as seqnum
from customers c cross join
(select count(*) as cnt from cars) cc
) c join
(select cc.*, row_number() over (order by dbms_random.random) as seqnum
from cars cc
) cc
on cc.seqnum = c.seqnum;
if no limit to use all cars and DB resources:
select customer_name||'-'||car_name result
from (
select u.name customer_name, c.name car_name,
row_number() over ( partition by u.name order by dbms_random.value ) ord
from customers u
cross join cars c
)
where ord = 1

Help with SQL QUERY OF JOIN+COUNT+MAX

I need a help constructung an sql query for mysql database. 2 Table as follows:
tblcities (id,name)
tblmembers(id,name,city_id)
Now I want to retrieve the 'city' details that has maximum number of 'members'.
Regards
SELECT tblcities.id, tblcities.name, COUNT(tblmembers.id) AS member_count
FROM tblcities
LEFT JOIN tblmembers ON tblcities.id = tblmembers.city_id
GROUP BY tblcities.id
ORDER BY member_count DESC
LIMIT 1
Basically: retrieve all cities and count how many members each has, sort by that member count in descending order, making the highest count first - then show only that first city.
Terrible, but that's a way of doing it:
SELECT * FROM tblcities WHERE id IN (
SELECT city_id
FROM tblMembers
GROUP BY city_id
HAVING COUNT(*) = (
SELECT MAX(TOTAL)
FROM (
SELECT COUNT(*) AS TOTAL
FROM tblMembers
GROUP BY city_id
) AS AUX
)
)
That way, if there is a tie, still you'll get all cities with the maximum number of members...
Select ...
From tblCities As C
Join (
Select city_id, Count(*) As MemberCount
From tblMembers
Order By Count(*) Desc
Limit 1
) As MostMembers
On MostMembers.city_id = C.id
select top 1 c.id, c.name, count(*)
from tblcities c, tblmembers m
where c.id = m.city_id
group by c.id, c.name
order by count(*) desc