SQL - extract column from subquery - sql

I'm working on a database meant for auctions and I would like get the id of all the winning bids. The hard part is extracting it from a subquery that returns 2 columns: the id and the amount.
It looks something like this:
SELECT id FROM Bid WHERE id IN (Select ID,max(amount) FROM Bid group by bid.idAuction)
Can I somehow extract just one column from the subquery? Any other sugestions to do this task are helpfull too.
Thank you!

Your query is close, but you need a correlated subquery to make this work:
SELECT b.id
FROM Bid b
WHERE b.amount = (SELECT max(amount)
FROM Bid b2
WHERE b2.idAuction = b.idAuction
);

SELECT id, maxBid.MAmount, Bid.Amount
FROM Bid
INNER JOIN (Select ID,max(amount) mamount FROM Bid group by bid.idAuction) MaxBid
on MaxBid.ID = Bid.ID
RDBMS and SQL operate most effectively in SET based operations. So in this case we generate a set based on ID and max bid. We then join it back to the base set so that only the max bids are treturned.

Related

Select entire row on group by aggregation

I'm having some struggle with something that should be a simple SQL query.
This is my initial database schema:
Also prepared the example in SQLFiddle
The query I've ended up with is:
select
b.ad_id,
b.auction_id,
max(b.amount) as max,
max(b.created_at) created_at
from bid b
where b.user_id = '601'
group by b.ad_id, b.auction_id
But in the result, I need the whole row from the bid table:
select
b.id,
b.ad_id,
b.auction_id,
max(b.amount) as max,
max(b.created_at) created_at
from bid b
where b.user_id = '601'
group by b.ad_id, b.auction_id
Which fails with: [42803] ERROR: column "b.id" must appear in the GROUP BY clause or be used in an aggregate function Position: 16. Cannot add the id field in the GROUP BY clause, because it will add some extra rows I don't need.
What I need is to select from the bid table the highest record (amount field) grouped by auction_id and ad_id.
I think I need to make some self inner join or subselect but right now I'm not able to write the SQL.
What I need is to select from the bid table the highest record (amount field) grouped by auction_id and ad_id
Take a look at DISTINCT ON in the docs. Your desired result would be obtained by the following query.
select DISTINCT ON (b.ad_id, b.auction_id)
b.id,
b.ad_id,
b.auction_id,
b.amount
b.created_at
from bid b
where b.user_id = '601'
ORDER BY b.ad_id, b.auction_id, b.amount DESC
If you want the most recent row for each auction for the given user, then you can use a correlated subquery to filter:
select b.*
from bid b
where b.user_id = '601' and
b.created_at = (select max(b2.created_at)
from bid b2
where b2.auction_id = b.auction_id and
b2.user_id = b.user_id
);
This seems like a sensible interpretation of what you want. I don't know if ad_id is needed.

SQL Server 2016 Sub Query Guidance

I am currently working on an assignment for my SQL class and I am stuck. I'm not looking for full code to answer the question, just a little nudge in the right direction. If you do provide full code would you mind a small explanation as to why you did it that way (so I can actually learn something.)
Here is the question:
Write a SELECT statement that returns three columns: EmailAddress, ShipmentId, and the order total for each Client. To do this, you can group the result set by the EmailAddress and ShipmentId columns. In addition, you must calculate the order total from the columns in the ShipItems table.
Write a second SELECT statement that uses the first SELECT statement in its FROM clause. The main query should return two columns: the Client’s email address and the largest order for that Client. To do this, you can group the result set by the EmailAddress column.
I am confused on how to pull in the EmailAddress column from the Clients table, as in order to join it I have to bring in other tables that aren't being used. I am assuming there is an easier way to do this using sub Queries as that is what we are working on at the time.
Think of SQL as working with sets of data as opposed to just tables. Tables are merely a set of data. So when you view data this way you immediately see that the query below returns a set of data consisting of the entirety of another set, being a table:
SELECT * FROM MyTable1
Now, if you were to only get the first two columns from MyTable1 you would return a different set that consisted only of columns 1 and 2:
SELECT col1, col2 FROM MyTable1
Now you can treat this second set, a subset of data as a "table" as well and query it like this:
SELECT
*
FROM (
SELECT
col1,
col2
FROM
MyTable1
)
This will return all the columns from the two columns provided in the inner set.
So, your inner query, which I won't write for you since you appear to be a student, and that wouldn't be right for me to give you the entire answer, would be a query consisting of a GROUP BY clause and a SUM of the order value field. But the key thing you need to understand is this set thinking: you can just wrap the ENTIRE query inside brackets and treat it as a table the way I have done above. Hopefully this helps.
You need a subquery, like this:
select emailaddress, max(OrderTotal) as MaxOrder
from
( -- Open the subquery
select Cl.emailaddress,
Sh.ShipmentID,
sum(SI.Value) as OrderTotal -- Use the line item value column in here
from Client Cl -- First table
inner join Shipments Sh -- Join the shipments
on Sh.ClientID = Cl.ClientID
inner join ShipItem SI -- Now the items
on SI.ShipmentID = Sh.ShipmentID
group by C1.emailaddress, Sh.ShipmentID -- here's your grouping for the sum() aggregation
) -- Close subquery
group by emailaddress -- group for the max()
For the first query you can join the Clients to Shipments (on ClientId).
And Shipments to the ShipItems table (on ShipmentId).
Then group the results, and count or sum the total you need.
Using aliases for the tables is usefull, certainly when you select fields from the joined tables that have the same column name.
select
c.EmailAddress,
i.ShipmentId,
SUM((i.ShipItemPrice - i.ShipItemDiscountAmount) * i.Quantity) as TotalPriceDiscounted
from ShipItems i
join Shipments s on (s.ShipmentId = i.ShipmentId)
left join Clients c on (c.ClientId = s.ClientId)
group by i.ShipmentId, c.EmailAddress
order by i.ShipmentId, c.EmailAddress;
Using that grouped query in a subquery, you can get the Maximum total per EmailAddress.
select EmailAddress,
-- max(TotalShipItems) as MaxTotalShipItems,
max(TotalPriceDiscounted) as MaxTotalPriceDiscounted
from (
select
c.EmailAddress,
-- i.ShipmentId,
-- count(*) as TotalShipItems,
SUM((i.ShipItemPrice - i.ShipItemDiscountAmount) * i.Quantity) as TotalPriceDiscounted
from ShipItems i
join Shipments s on (s.ShipmentId = i.ShipmentId)
left join Clients c on (c.ClientId = s.ClientId)
group by i.ShipmentId, c.EmailAddress
) q
group by EmailAddress
order by EmailAddress
Note that an ORDER BY is mostly meaningless inside a subquery if you don't use TOP.

SQL grouping. How to select row with the highest column value when joined. No CTEs please

I've been banging my head against the wall for something that I think should be simple but just cant get to work.
I'm trying to retrieve the row with the highest multi_flag value when I join table A and table B but I can't seem to get the SQL right because it returns all the rows rather than the one with the highest multi_flag value.
Here are my tables...
Table A
Table B
This is almost my desired result but only if I leave out the value_id row
SELECT CATALOG, VENDOR_CODE, INVLINK, NAME_ID, MAX(multi_flag) AS multiflag
FROM TBLINVENT_ATTRIBUTE AS A
INNER JOIN TBLATTRIBUTE_VALUE AS B
ON A.VALUE_ID = B.VALUE_ID
GROUP BY CATALOG, VENDOR_CODE, INVLINK, NAME_ID
ORDER BY CATALOG DESC
This is close to what I want to retreive but not quite notice how it returns unique name_id and the highest multi_flag but I also need the value_id that belongs to such multi_flag / name_id grouping...
If I include the value_id in my SQL statement then it returns all rows and is no longer grouped
Notic ein the results below how it no longer returns the row for the highest multi_flag and how all the different values for name_id (Ex. name_id 1) are also returned
You can choose to use a sub-query, derived table or CTE to solve this problem. Performance will be depending on the amount of data you are querying. To achieve your goal of getting the max multiflag you must first get the max value based on the grouping you want to achieve this you can use a CTE or sub query. The below CTE will give the max multi_flag by value that you can use to get the max multi_flag and then you can use that to join back to your other tables. I have three joins in this example but this can be reduce and as far a performance it may be better to use a subquery but you want know until you get the se the actual execution plans side by side.
;with highest_multi_flag as
(
select value_id, max(multi_flag) AS multiflag
FROM TBLINVENT_ATTRIBUTE
group by value_id
)
select A.CATALOG, a.VENDOR_CODE, a.INVLINK, b.NAME_ID,m.multiflag
from highest_multi_flag m
inner join TBLINVENT_ATTRIBUTE AS A on a.VALUE_ID =b. m.VALUE_ID
INNER JOIN TBLATTRIBUTE_VALUE AS B ON m.VALUE_ID = B.VALUE
You can use Lateral too, its an other solution
SELECT
A.CATALOG, A.VENDOR_CODE, A.INVLINK, B.NAME_ID, M.maxmultiflag
FROM TBLINVENT_ATTRIBUTE AS A
inner join lateral
(
select max(B.multi_flag) as maxmultiflag from TBLINVENT_ATTRIBUTE C
where A.VALUE_ID = C.VALUE_ID
) M on 1=1
INNER JOIN TBLATTRIBUTE_VALUE AS B ON M.maxmultiflag = B.VALUE

Filteration of Rows based on a column value

I have a table called AssetTable which stores AssetId
I have another table called AssetBookTable which stores two fields BookId and CurrentOperationsTax
Another table AssetBook that stores AssetId and BookId as foriegn keys.
Now, I have a situation in which I have to filter records based on
AssetBookTable.CurrentOperationsTax
This, is what I am getting now,
And this, is what I want:
**
The logic is that I want only one BookId per AssetId that has either Current/Operation/Tax for CurrentOperationsTax field.
**
Here's the SQL fiddle written so far:
SQLFiddle
Any help is greatly appreciated.
You can simple remove the GROUP BY BookID. But of course you then need to aggregate the BookID some how. Here using the MIN() function:
SELECT
ab.AssetId
,MIN(ab.BookId) as BookID
,abt.CuurentOperationsTax
FROM
AssetBook ab
JOIN AssetTable at ON at.AssetId = ab.AssetId
JOIN AssetBookTable abt ON abt.BookId = ab.BookId
GROUP BY
ab.AssetId
,abt.CuurentOperationsTax
http://sqlfiddle.com/#!6/e3477/42
I prefer using the ROW_NUMBER approach. It does require the use of a subquery. In summary it is grouping the records by the CuurentOperationsTax number and ordering by book id and giving the row number for each grouping. Then in the outer select I filter out how many I want for each grouping. In this example just 1.
SELECT AssetId
,BookId
,CuurentOperationsTax
,RowNum
FROM (
SELECT
ab.AssetId
,ab.BookId
,abt.CuurentOperationsTax
,ROW_NUMBER() OVER(PARTITION BY abt.CuurentOperationsTax ORDER BY ab.BookId) AS RowNum
FROM
AssetBook ab
JOIN AssetTable AT ON AT.AssetId = ab.AssetId
JOIN AssetBookTable abt ON abt.BookId = ab.BookId
) AS b
WHERE b.RowNum = 1

Using SQL(ite) how do I find the lowest unique child for each parent in a one to many relationship during a JOIN?

I have two tables with a many to one relationship which represent lots and bids within an auction system. Each lot can have zero or more bids associated with it. Each bid is associated with exactly one lot.
My table structure (with irrelevant fields removed) looks something like this:
For one type of auction the winning bid is the lowest unique bid for a given lot.
E.g. if there are four bids for a given lot: [1, 1, 2, 4] the lowest unique bid is 2 (not 1).
So far I have been able to construct a query which will find the lowest unique bid for a single specific lot (assuming the lot ID is 123):
SELECT id, value FROM bid
WHERE lot = 123
AND amount = (
SELECT value FROM bid
WHERE lot = 123
GROUP BY value HAVING COUNT(*) = 1
ORDER BY value
)
This works as I would expect (although I'm not sure it's the most graceful approach).
I would now like to construct a query which will get the lowest unique bids for all lots at once. Essentially I want to perform a JOIN on the two tables where one column is the result of something similar to the above query. I'm at a loss as to how to use the same approach for finding the lowest unique bid in a JOIN though.
Am I on the wrong track with this approach to finding the lowest unique bid? Is there another way I can achieve the same result?
Can anyone help me expand this query into a JOIN?
Is this even possible in SQL or will I have to do it in my application proper?
Thanks in advance.
(I am using SQLite 3.5.9 as found in Android 2.1)
You can use group by with a "having" condition to find the set of bids without duplicate amounts for each lot.
select lotname, amt
from lot inner join bid on lot.id = bid.lotid
group by lotname, amt having count(*) = 1
You can in turn make that query an inline view and select the lowest bid from it for each lot.
select lotname, min(amt)
from
(
select lotname, amt
from lot inner join bid on lot.id = bid.lotid
group by lotname, amt having count(*) = 1
) as X
group by X.lotname
EDIT: Here's how to get the bid id using this approach, using nested inline views:
select bid.id as WinningBidId, Y.lotname, bid.amt
from
bid
join
(
select x.lotid, lotname, min(amt) as TheMinAmt
from
(
select lot.id as lotid, lotname, amt
from lot inner join bid on lot.id = bid.lotid
group by lot.id, lotname, amt
having count(*)=1
) as X
group by x.lotid, x.lotname
) as Y
on Y.lotid = bid.lotid and Y.TheMinAmt = Bid.amt
I think you need some subqueries to get to your desired data:
SELECT [b].[id] AS [BidID], [l].[id] AS [LotID],
[l].[Name] AS [Lot], [b].[value] AS [BidValue]
FROM [bid] [b]
INNER JOIN [lot] [l] ON [b].[lot] = [l].[id]
WHERE [b].[id] =
(SELECT TOP 1 [min].[id]
FROM [bid] [min]
WHERE [min].[lot] = [b].[lot]
AND NOT EXISTS(SELECT *
FROM [bid] [check]
WHERE [check].[lot] = [min].[lot]
AND [check].[value] = [min].[value]
AND [check].[id] <> [min].[id])
ORDER BY [min].[value] ASC)
The most inner query (within the exists) checks if there are no other bids on that lot, having the same value.
The query in the middle (top 1) determines the minimum bid of all unique bids on that lot.
The outer query makes this happen for all lots, that have bids.
SELECT lot.name, ( SELECT MIN(bid.value) FROM bid Where bid.lot = lot.ID) AS MinBid
FROM Lot INNER JOIN
bid on lot.ID = bid.ID
If I understand you correctly this will give you everylot and their smallest bid