Group BY with multiple selects (Oracle) - sql

Im having a problem using group by with multiple selects. I want to select the minimum bid offer price for each Auction. But I also have to get the name of the user who made that Bid. As you can see in the results, I get multiple results for each auction, and if I remove elements from the Groupby statement I get an error message. How can I groupy by ID_Auction and still show the name of the User? Thanks for the help
SELECT A.ID_AUCTION,MIN(B.PRICE) AS PRICE,U.NAME
FROM AUCTION A,BIDS B,USERS U,PRODUCT P
WHERE P.TYPE='CocaCola'
--Joins
and A.ID_AUCTION=B.ID_AUCTION
and BID.ID_USER=U.ID_USER
and A.ID_PRODUCT=P.ID_PRODUCT
GROUP BY A.ID_AUCTION,U.NAME;
ID_AUCTION PRICE NAME
---------- ---------- -------------- ------------------------------------------
27 25 Andrew
28 40 John
27 30 Michael
28 35 Peter
The Output I Desire :
ID_AUCTION PRICE NAME
---------- ---------- -------------- ------------------------------------------
27 25 Andrew
28 35 Peter

SELECT A.ID_AUCTION,MIN(B.PRICE) AS PRICE,U.NAME
FROM AUCTION A,BIDS B,USERS U,PRODUCT P
WHERE P.TYPE='CocaCola'
--Joins
and A.ID_AUCTION=B.ID_AUCTION
and BID.ID_USER=U.ID_USER
and A.ID_PRODUCT=P.ID_PRODUCT
GROUP BY A.ID_AUCTION,U.NAME;
Should probably be like:
SELECT A.ID_AUCTION, U.NAME, B.PRICE as PRICE
from AUCTION A
inner join (SELECT ID_AUCTION, NAME, MIN(Price) from Bids order by 1,2,3) B
ON a.ID_AUCTION = b.ID_AUCTION
inner join Users U
ON B.ID_USER = U.ID_USER
inner join Product P
ON A.ID_PRODUCT = P.ID_PRODUCT
WHERE P.TYPE = 'CocaCola'
GROUP BY A.ID_AUCTION, U.NAME
I've never seen joins work the way you did it, but that might be an Oracle thing...actually I think that's considered bad practice in most SQL arenas...

Give this a try, this will do the trick. It is ok with the coding style. Some old systems still work well with the ANSI-89 SQL style:
SELECT A.ID_AUCTION,
B.PRICE,
U.NAME
FROM AUCTION A,
BIDS B,
USERS U,
PRODUCT P
WHERE P.TYPE='CocaCola' --Joins
AND A.ID_AUCTION=B.ID_AUCTION
AND B.ID_USER=U.ID_USER
AND A.ID_PRODUCT=P.ID_PRODUCT
AND B.PRICE = (SELECT MIN(bids.PRICE) FROM BIDS --get only the bid with MIN price
WHERE bids.ID_AUCTION = A.ID_AUCTION);

select * from
(
SELECT A.ID_AUCTION,B.PRICE,U.NAME,
Dense_Rank() over (partition by ID_AUCTION order by Price Asc)AS Rank
FROM
AUCTION A
JOIN
BIDS B
ON A.ID_AUCTION=B.ID_AUCTION
JOIN
USERS U
ON BID.ID_USER=U.ID_USER
JOIN
PRODUCT P
ON A.ID_PRODUCT=P.ID_PRODUCT
WHERE P.TYPE='CocaCola')
where rank='1'

First should just group by A.ID_AUCTION and second on your select you could try a min(U.NAME)
SELECT A.ID_AUCTION,MIN(B.PRICE) AS PRICE,min(U.NAME)
FROM AUCTION A,BIDS B,USERS U,PRODUCT P
WHERE P.TYPE='CocaCola'
--Joins
and A.ID_AUCTION=B.ID_AUCTION
and BID.ID_USER=U.ID_USER
and A.ID_PRODUCT=P.ID_PRODUCT
GROUP BY A.ID_AUCTION;

Related

Select highest value linked to title - sql

I am trying to select the highest pair from a table that has been created by joining (combining) two tables. I guess I should use a nested query but I'm not sure how.
I also came around a similar question that seems a bit less complex, but I am struggling to implement it into my problem.
Similar question: How to select max timestamp from each currency pair SQL?
My tables:
Book:
title
publisher
price
sold
book1
A
5
300
book2
B
15
150
book3
A
8
350
Publisher:
code
name
A
ABook
B
BBook
C
CBook
My query:
SELECT b.titel, p.name, max(b.price*b.sold) as 'Revenue"
FROM publisher p, book b
WHERE p.code = b.publisher
Gives:
title
publisher
Revenue
book1
ABook
1500
book2
BBook
2250
book3
ABook
2800
Desired output:
title
publisher
book2
BBook
book3
ABook
How to alter my query to get the highest revenue per book title and the corresponding publisher?
You can use this query:
SELECT b.titel, p.name
FROM publisher p, book b
WHERE p.code = b.publisher
order by b.price*b.sold desc
OR
select abc.titel,abc.name FROM (
select b.titel, p.name, max( b.price*b.sold) as balance
FROM publisher p, book b
WHERE p.code = b.publisher
group by b.titel, p.name ) abc order by abc.balance desc
You can use row_number window function to select the appropriate row for each group.
Your desired results don't align with your description (do you want a revenue column or not?), however this produces your desired output. Note the use of modern (for 30 years) ansi join syntax:
with sold as (
select *, Row_Number() over(partition by publisher order by (price * sold) desc) rn
from book b join publisher p on p.code=b.publisher
)
select title, name Publusher
from sold
where rn=1
I solved it by using a nested query and max function
select b.title, p.name
from book b join publisher p on p.code = b.publisher
where b.sold*b.price = (select max(sold*price)
from book t2
where t2.publisher = b.publisher
)

Most popular pairs of shops for workers from each company

I've got 2 tables, one with sales and one with companies:
Sales Table
Transaction_Id Shop_id Sale_date Client_ID
92356 24234 11.09.2018 12356
92345 32121 11.09.2018 32121
94323 24321 11.09.2018 21231
94278 45321 11.09.2018 42123
Company table
Client_ID Company_name
12345 ABC
13322 ABC
32321 BCD
22221 BCD
What I want to achieve is distinct count of Clients from each Company for each pair of shops(Clients who had at least 1 transaction in both of shops) :
Shop_Id_1 Shop_id_2 Company_name Count(distinct Client_id)
12356 12345 ABC 31
12345 14278 ABC 23
14323 12345 BCD 32
14278 12345 BCD 43
I think that I have to use self join, but my queries even with filter for one week is killing DB, any thoughts on that? I'm using Microsoft SQL server 2012.
Thanks
I think this is a self-join and aggregation, with a twist. The twist is that you want to include the company in each sales record, so it can be used in the self-join:
with sc as (
select s.*, c.company_name
from sales s join
companies c
on s.client_id = c.client_id
)
select sc1.shop_id, sc2.shop_id, sc1.company_name, count(distinct sc1.client_id)
from sc sc1 join
sc sc2
on sc1.client_id = sc2.client_id and
sc1.company_name = sc2.company_name
group by sc1.shop_id, sc2.shop_id, sc1.company_name;
I think there are some issues with your question. I interpreted it as such that the company table contains the shop ID's, not the ClienId's.
First you can create a solution to get the shops as rows for each company. Here I chose a maximum of 5 shops per company. Don't forget the semicolon in the previous statement before the cte's.
WITH CTE_Comp AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY CompanyName ORDER BY ShopID) AS RowNumb
FROM Company AS C
)
SELECT C1.ShopID,
C2.ShopID AS ShopID_2,
C3.ShopID AS ShopID_3,
C4.ShopID AS ShopID_4,
C5.ShopID AS ShopID_5,
C1.CompanyName
INTO ShopsByCompany
FROM CTE_Comp AS C1
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C2.CompanyName AND RowNumb = 2
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C3.CompanyName AND RowNumb = 3
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C4.CompanyName AND RowNumb = 4
LEFT JOIN CTE_Comp AS C2 ON C1.CompanyName= C5.CompanyName AND RowNumb = 5
WHERE C1.RowNumb = 1
After that, in a few steps, I think you could get the desired result:
WITH ClientsPerShop AS
(
SELECT ShopID,
COUNT (DISTINCT ClientID) AS TotalClients
FROM Sales
GROUP BY ShopID
)
, ClienstsPerCompany AS
(
SELECT CompanyName,
SUM (TotalClients) AS ClientsPerComp
FROM Company AS C
INNER JOIN ClientsPerShop AS CPS ON C.ShopID = CPS.ShopID
GROUP BY CompanyName
)
SELECT *
FROM ClienstsPerCompany AS CPA
INNER JOIN ShopsByCompany AS SBC ON SBC.CompanyName = CPA.CompanyName
Hopefully this will bring you closer to your solution, best of luck!

How can I show all "article" which have more than 3 "bids"?

I wanna show the "ArticleName" of all "offers" that have more than 3 "bids". The number of the "Bids" should be output.
I don't know how I can write it down. But I think I know the Logic. It should count the same number of the Table "bid" and the column "OID" and in the end it should paste the number which is more than 3.
Picture:
Well that's easy enough:
Select ArticleName
, count(*) NumberOfBids
from Offer o
join Bid b
on b.oid = o.oid
group by ARticleName
having count(*) >= 3
SELECT * FROM (
SELECT o.ArticleName, count(b.BID) as numberOfBids
FROM Offer as o INNER JOIN bid as b ON o.oid = b.oid
GROUP BY o.ArticleName
) as c
WHERE c.numberOfBids > 3

Select all customers loyal to one company?

I've got tables:
TABLE | COLUMNS
----------+----------------------------------
CUSTOMER | C_ID, C_NAME, C_ADDRESS
SHOP | S_ID, S_NAME, S_ADDRESS, S_COMPANY
ORDER | S_ID, C_ID, O_DATE
I want to select id of all customers who made order only from shops of one company - 'Samsung' ('LG', 'HP', ... doesn't really matter, it's dynamic).
I've come only with one solution, but I consider it ugly:
( SELECT DISTINCT c_id FROM order JOIN shop USING(s_id) WHERE s_company = "Samsung" )
EXCEPT
( SELECT DISTINCT c_id FROM order JOIN shop USING(s_id) WHERE s_company != "Samsung" );
Same SQL queries, but reversed operator. Isn't there any aggregate method which solves such query better?
I mean, there could be millions of orders(I don't really have orders, I've got something that occurs more often).
Is it efficient to select thousands of orders and then compare them to hundreds of thousands orders which have different company? I know, that it compares sorted things, so it's O( m + n + sort(n) + sort(m) ). But that's still large for millions of records, or isn't?
And one more question. How could I select all customer values (name, address). How can I join them, can I do just
SELECT CUSTOMER.* FROM CUSTOMER JOIN ( (SELECT...) EXCEPT (SELECT...) ) USING (C_ID);
Disclaimer: This question ain't homework. It's preparation for the exam and desire to things more effective. My solution would be accepted at exam, but I like effective programming.
I like to approach this type of question using group by and a having clause. You can get the list of customers using:
select o.c_id
from orders o join
shops s
on o.s_id = o.s_id
group by c_id
having min(s.s_company) = max(s.s_company);
If you care about the particular company, then:
having min(s.s_company) = max(s.s_company) and
max(s.s_company) = 'Samsung'
If you want full customer information, you can join the customers table back in.
Whether this works better than the except version is something that would have to be tested on your system.
How about a query that uses no aggregate functions like Min and Max?
select C_ID, S_ID
from shop
group by C_ID, S_ID;
Now we have a distinct list of customers and all the companies they shopped at. The loyal customers will be the ones who only appear once in the list.
select C_ID
from Q1
group by C_ID
having count(*) = 1;
Join back to the first query to get the company id:
with
Q1 as(
select C_ID, S_ID
from shop
group by C_ID, S_ID
),
Q2 as(
select C_ID
from Q1
group by C_ID
having count(*) = 1
)
select Q1.C_ID, Q1.S_ID
from Q1
join Q2
on Q2.C_ID = Q1.C_ID;
Now you have a list of loyal customers and the one company each is loyal to.

In SQL how do I write a query to return 1 record from a 1 to many relationship?

Let's say I have a Person table and a Purchases table with a 1 to many relationship. I want to run a single query that returns this person and just their latest purchase. This seems easy but I just can't seem to get it.
select p.*, pp.*
from Person p
left outer join (
select PersonID, max(PurchaseDate) as MaxPurchaseDate
from Purchase
group by PersonID
) ppm
left outer join Purchase pp on ppm.PersonID = pp.PersonID
and ppm.MaxPurchaseDate = pp.PurchaseDate
where p.PersonID = 42
This query will also show the latest purchase for all users if you remove the WHERE clause.
Assuming you have something like a PurchaseDate column and want a particular person (SQL Server):
SELECT TOP 1 P.Name, P.PersonID, C.PurchaseDescription FROM Persons AS P
INNER JOIN Purchases AS C ON C.PersonID = P.PersonID
WHERE P.PersonID = #PersonID
ORDER BY C.PurchaseDate DESC
Many Databases preform the "Limit or Top" command in different ways. Here is a reference http://troels.arvin.dk/db/rdbms/#select-limit and below are a few samples
If using SQL Server
SELECT TOP 1
*
FROM Person p
INNER JOIN Purchases pc on pc.PersonID = P.PersonID
Order BY pc.PurchaseDate DESC
Should work on MySQL
SELECT
*
FROM Person p
INNER JOIN Purchases pc on pc.PersonID = P.PersonID
Order BY pc.PurchaseDate DESC
LIMIT 1
Strictly off the top of my head!...If it's only one record then...
SELECT TOP 1 *
FROM Person p
INNER JOIN Purchases pu
ON p.ID = p.PersonId
ORDER BY pu.OrderDate
WHERE p.ID = *thePersonYouWant*
otherwise...
SELECT TOP 1 *
FROM Person p
INNER JOIN
(
SELECT TOP 1 pu.ID
FROM Purchases pu
ON pu.PersonID = p.Id
ORDER BY pu.OrderDate
) sq
I think! I haven't got access to a SQL box right now to test it on.
Without knowing your structure at all, or your dbms, you would order the results descending by the purchase date/time, and return only the first joined record.
Try TOP 1 With an order by desc on date. Ex:
CREATE TABLE #One
(
id int
)
CREATE TABLE #Many
(
id int,
[date] date,
value int
)
INSERT INTO #One (id)
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3
INSERT INTO #Many (id, [date], value)
SELECT 1, GETDATE(), 1 UNION ALL
SELECT 1, DATEADD(DD, 1 ,GETDATE()), 3 UNION ALL
SELECT 1, DATEADD(DD, -1 ,GETDATE()), 0
SELECT TOP 1 *
FROM #One O
JOIN #Many M ON O.id = M.id
ORDER BY [date] DESC
If you want to select the latest purchase for each person, that would be:
SELECT PE.ID, PE.Name, MAx(PU.pucrhaseDate) FROM Persons AS PE JOIN PURCHASE as PU ON PE.ID = PU.Person_ID
If you want to have all persons also those who have no purchases, you need to use LEFT JOIN.
I think you need one more table called Items for example.
The PERSONS table would uniquely define each person and all their attributes, while the ITEMS table would uniquely define each items and their attributes.
Assume the following:
Persons |Purchases |Items
PerID PerName |PurID PurDt PerID ItemID |ItemID ItemDesc ICost
101 Joe Smith |201 101107 101 301 |301 Laptop 500
|202 101107 101 302 |302 Desktop 699
102 Jane Doe |203 101108 102 303 |303 iPod 199
103 Jason Tut |204 101109 101 304 |304 iPad 499
|205 101109 101 305 |305 Printer 99
One Person Parent may tie to none, one or many Purchase Child.
One Item Parent may tie to none, one or many Purchase Child.
One or more Purchases Children will tie to one Person Parent, and one Item Parent.
select per.PerName as Name
, pur.PurDt as Date
, itm.ItemDesc as Item
, itm.ICost as Cost
from Persons per
, Purchases pur
, Items itm
where pur.PerID = per.PerID -- For that Person
and pur.ItemID = itm.ItemID -- and that Item
and pur.PurDt = -- and the purchase date is
( Select max(lst.PurDt) -- the last date
from Purchases lst -- purchases
where lst.PerID = per.PerID ) -- for that person
This should return:
Name Date Item Cost
Joe Smith 101109 Ipad 499
Joe Smith 101109 Printer 99
Jane Doe 101108 iPod 199