SQL MAX of column including its primary key - sql

Short:
From below sql select I get the cart_id and the value of the maximum valued item in that cart.
SELECT CartItems.cart_id, MAX(ItemValues.value)
FROM CartItems
INNER JOIN ItemValues
ON CartItems.item_id=ItemValues.item_id
GROUP BY CartItems.cart_id
but I also need item_id for that item (ItemValues.item-id).
Long:
Two tables, CartItems, ItemValues (and their respective Carts, Items, irrelevant here).
Each cart can have several items wheras each item has one value defined in ItemValues.
Each item belongs to one cart.
The value of a cart is the value of the item with maximum value within its cart.
How do I select cart-id, max(item-value) and it's corresponding item-id?
For instance cart-id A contains item-id X with value 10 and item-id Y with value 90.
With above sql select I get, A, 90
What I need is
A, Y, 90
platform: MS SQL

In MS SQL and Oracle:
SELECT *
FROM
(
SELECT ci.*, iv.*,
ROW_NUMBER() OVER (PARTITION BY CartItems.cart_id ORDER BY ItemValues.value DESC)
FROM CartItems ci
INNER JOIN ItemValues iv
ON CartItems.item_id=ItemValues.item_id
) s
WHERE rn = 1
In MySQL:
SELECT
FROM
(
SELECT ci.*,
(
SELECT id
FROM ItemValues iv
WHERE iv.item_id = ci.item_id
ORDER BY
value DESC
LIMIT 1
) AS maxitem
FROM CartItems ci
) iv, ItemValues ivo
WHERE ivo.id = iv.maxitem

This code was written for Oracle, but should be compatible with most SQL versions:
This gets the max(high_val) and returns its key.
select high_val, my_key
from (select high_val, my_key
from mytable
where something = 'avalue'
order by high_val desc)
where rownum <= 1
What this says is: Sort mytable by high_val descending for values where something = 'avalue'. Only grab the top row, which will provide you with the max(high_val) in the selected range and the my_key to that table.

Related

How to deselect duplicate entries in a query?

I've got a query like this:
SELECT *
FROM RecipeTable, RecipeIngredientTable, SyncRecipeIngredientTable
WHERE RecipeTable.recipe_id = SyncRecipeIngredientTable.recipe_id
AND RecipeIngredientTable.recipe_ingredient_id =
SyncRecipeIngredientTable.recipe_ingredient_id
AND RecipeIngredientTable.recipe_item_name in ("ayva", "pirinç", "su")
GROUP by RecipeTable.recipe_id
HAVING COUNT(*) >= 3;
and this query returns the result like this:
As you can see in the image there is 3 duplicate, unnecessary entries (no, i can't delete them because of the multiple foreign keys). How can I deselect these duplicate entries from the result query? In the end I want to return 6 entries not 9.
What you want to eliminate in the result set is not duplication of recipe_id values but recipe_name values.
You just need to group(partition) by recipe_name through use of ROW_NUMBER() analytic function :
SELECT recipe_id, author_name ...
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY recipe_name) AS rn,
sr.recipe_id, author_name ...
FROM SyncRecipeIngredientTable sr
JOIN RecipeIngredientTable ri
ON ri.recipe_ingredient_id = sr.recipe_ingredient_id
JOIN RecipeTable rt
ON rt.recipe_id = sr.recipe_id
WHERE ri.recipe_item_name in ("ayva", "pirinç", "su")
)
WHERE rn = 1
This way, you can pick only one of the records with rn=1 (ORDER BY Clause might be added to that analytic function after PARTITION BY clause if spesific record is needed to be picked)

Oracle - return only the first row for each product

In the first SELECT statement, the report grabs inv details for all products with shipment activity. There is then a UNION that connect another SELECT statement to grab products without activity from the last calendar year.
However, the records that are returned in the second SELECT statement have multiple header_id’s and therefore multiple lines… instead of single lines like the first SELECT statement. Do you know how to pull only the first header_id of each record in the second SELECT statement?
Code and example result set below. In data, product #7 should only list the row for header_id 1372288 which is the last ID entered into the DB.
select 3 sort_key, header_Id,location_id,nlasinv.product,
start_inv,produced produced_inv,stored,from_stock,shipped,
(start_inv + produced + stored) - (from_stock + shipped) end_inv,nlas_ops_mtd_prodsize(111,nlasinv.product,'31-DEC-19'), nlas_ops_mtd_shipsize(111,nlasinv.product,'31-DEC-19'),nlas_ops_ytd_prodsize(111,nlasinv.product,'31-DEC-19'), nlas_ops_ytd_shipsize(111,nlasinv.product,'31-DEC-19')
from nlas_header inv,
nlas_inventory nlasinv
where nlasinv.header_id = 1372168
and inv.id = nlasinv.header_id
union
select distinct
3 sort_key,header_Id,location_id,nlasinv.product,
start_inv,produced produced_inv,stored,from_stock,shipped,
(start_inv + produced + stored) - (from_stock + shipped) end_inv,nlas_ops_mtd_prodsize(111,nlasinv.product,'31-DEC-19'),
nlas_ops_mtd_shipsize(111,nlasinv.product,'31-DEC-19'),nlas_ops_ytd_prodsize(111,nlasinv.product,'31-DEC-19'),
nlas_ops_ytd_shipsize(111,nlasinv.product,'31-DEC-19')
from
nlas_inventory nlasinv,
nlas_header hdr
where
nlasinv.header_id = hdr.id
and hdr.location_id = 409
and hdr.observation_date >= trunc(to_date('31-DEC-19','dd-mon-rr'),'year')
and nlasinv.product not in
(select distinct product from
nlas_header h,
nlas_inventory i
where i.header_id = 1372168)
order by product, header_id des
c
I don't know what your query has to do with the "table" data that you show. But you seem to want row_number():
select t.*
from (select t.*, row_number() over (partition by product order by header_id desc) as seqnum
from t
) t
where seqnum = 1;
If that query is being used to generate the data, then just wrap it in a CTE.
This can also be accomplished by adding a self join in your where clause
and hdr.header_id = (
select max(hdr2.header_id)
from nlas_header hdr2
where hdr2.location_id = hdr.location_id
and hdr2.product_id = hdr.product_id)

selecting top 5 client by ticket sales with multiple records

select client#,
SUM(ticketpurchase.totalamount) value
from ops$yyang00.ticketpurchase,
ops$yyang00.performance,
ops$yyang00.production
where ticketpurchase.per# = performance.per# and
performance.P#=production.P# and
season#=1
group by client#
order by value desc;
How to select top 5 client in this for each season by ticket sale value one value can include multiple client..
Please try this code for sql server.
Select * from (
select client#,SUM(ticketpurchase.totalamount) value,
row_number() over(order by SUM(ticketpurchase.totalamount)) rowNo
from ops$yyang00.ticketpurchase,
ops$yyang00.performance,
ops$yyang00.production
where ticketpurchase.per# = performance.per# and
performance.P#=production.P# and
season#=1
group by client#
) as t
where rowNo <=5
Try this: (If you are using SQL Server)
select top 5 from (
select client#,
SUM(ticketpurchase.totalamount) value
from ops$yyang00.ticketpurchase,
ops$yyang00.performance,
ops$yyang00.production
where ticketpurchase.per# = performance.per# and
performance.P#=production.P# and
season#=1
group by client# )
order by value desc
Note: If you want the least 5 records, remove the desc from order by

Issue selecting Category and SubCategory of an Item while creating one delimited string per row of a table

Some Context: The DB is Oracle. I am trying to create one delimited string per row of a table. The delimited string needs to contain two of the item's categories (if available, there will always be one category at a minimum). There are 3 tables, ITEM, ITEM_CAT and ITEM_ITEM_CAT. The ITEM table holds all the items. The ITEM_CAT table holds all the possible item categories. The ITEM_ITEM_CAT holds all the mappings between the item IDs and the category IDs, a key value table essentially.
I have created the below SQL which is able to get the delimited string for a specific item, but I need a query which can run against the entire table.
SELECT 'ITEM'||'%#'|| outerTable.ITEM_ID ||'%#'||
(SELECT midTable.item_cat_nam
FROM
(SELECT innerTable.item_cat_nam AS item_cat_nam, innerTable.item_id AS item_id, ROWNUM AS rn
FROM
(SELECT ic.ITEM_CAT_NAM AS item_cat_nam, i.ITEM_ID AS item_id
FROM ITEM_CAT ic, ITEM_ITEM_CAT iic, ITEM i
WHERE i.ITEM_ID = iic.ITEM_ID
AND iic.ITEM_CAT_CD = ic.ITEM_CAT_CD
AND 287484 = i.item_id
) innerTable
) midTable
WHERE rn = 1
) ||'%#'||
(SELECT midTable.item_cat_nam
FROM
(SELECT innerTable.item_cat_nam AS item_cat_nam, innerTable.item_id AS item_id, ROWNUM AS rn
FROM
(SELECT ic.ITEM_CAT_NAM AS item_cat_nam, i.ITEM_ID AS item_id
FROM ITEM_CAT ic, ITEM_ITEM_CAT iic, ITEM i
WHERE i.ITEM_ID = iic.ITEM_ID
AND iic.ITEM_CAT_CD = ic.ITEM_CAT_CD
AND 287484 = i.item_id
) innerTable
) midTable
WHERE rn = 2
)
FROM OFR outerTable
WHERE outerTable.ITEM_ID = 287484;
I need to be able to pass the outer table's ITEM_ID down into the last inner join. I could do this when I only need the category (via the below SQL statement, only one inner join needed), but with the introduction of multiple categories; I need rownum (to get multiple categories) which then needs more inner joins and I can't seem to pass the ITEM_ID down more than one inner join, and here lies the problem...
SELECT 'ITEM'||'%#'|| outerTable.OFR_ID ||'%#'||
(SELECT ic.ITEM_CAT_NAM
FROM ITEM_CAT ic, ITEM_ITEM_CAT iic, ITEM i
WHERE i.ITEM_ID = iic.ITEM_ID
AND iic.ITEM_CAT_CD = ic.ITEM_CAT_CD
AND outerTable.OFR_ID = i.item_id
AND rownum = 1
) innerTable
FROM OFR outerTable;
Can anyone help with this?
Thank you in advance for any assitance.
No worries. You need something like this...
SELECT 'ITEM' || '%#' || Item_ID || '%#' || CatName1 || '%#' || CatName2
FROM outerTable
INNER JOIN (
SELECT
Item_ID,
MAX(CASE WHEN rn = 1 THEN Item_Cat_Nam ELSE NULL END) CatName1,
MAX(CASE WHEN rn = 2 THEN Item_Cat_Nam ELSE NULL END) CatName2
FROM (
SELECT
Item_ID,
Item_Cat.Item_Cat_Nam,
ROW_NUMBER() OVER (PARTITION BY Item_ID ORDER BY Item_ID) rn
FROM Item
INNER JOIN Item_Item_Cat USING (Item_ID)
INNER JOIN Item_Cat USING (Item_Cat_Cd)
) GROUP BY Item_ID
) USING (Item_ID)
The innermost query uses the ROW_NUMBER function to assign 1, 2, 3, etc. to every category found for each item. The PARTITION BY restarts the numbering at 1 for each item. The ORDER BY is required so I used Item_ID because hey, why not? If you have a preferred column to order by just use that - it will be used to assign the row numbers. The inner query will output something like this:
Item_ID Item_Cat_Nam rn
------- ------------ --
1 Category aa 1
1 Category xy 2
1 Category ef 3
2 Category xy 1
2 Category ax 2
3 Category ef 1
The query surrounding the innermost query uses MAX to flatten the first two rn values for each Item_ID into a single row. The Item_Cat_Nam for rn=1 goes to the CatName1 column and the Item_Cat_Nam for rn=2 goes to the CatName2 column. When it's fed the results shown above you'll end up with this:
Item_ID CatName1 CatName2
------- ----------- -----------
1 Category aa Category xy (note Category ef is rn=3 so it's ignored)
2 Category xy Category ax
3 Category ef (note only one row for Item_ID 3)
Then the very outer query just concatenates everything.
One other thing: I used the "JOIN ... USING" syntax because in this case it lets you eliminate all of the aliases (innerTable, i, ic, iic, midTable, etc.). That's purely because I'm more comfortable with it so it helped me figure this out a lot quicker. You should feel free to use your own join style - after all you'll be the one stuck maintaining it :)

Getting row number for query

I have a query which will return one row. Is there any way I can find the row index of the row I'm querying when the table is sorted?
I've tried rowid but got #582 when I was expecting row #7.
Eg:
CategoryID Name
I9GDS720K4 CatA
LPQTOR25XR CatB
EOQ215FT5_ CatC
K2OCS31WTM CatD
JV5FIYY4XC CatE
--> C_L7761O2U CatF <-- I want this row (#5)
OU3XC6T19K CatG
L9YKCYAYMG CatH
XKWMQ7HREG CatI
I've tried rowid with unexpected results:
SELECT rowid FROM Categories WHERE CategoryID = 'C_L7761O2U ORDER BY Name
EDIT: I've also tried J Cooper's suggestion (below), but the row numbers just aren't right.
using (var cmd = conn.CreateCommand()) {
cmd.CommandText = string.Format(#"SELECT (SELECT COUNT(*) FROM Recipes AS t2 WHERE t2.RecipeID <= t1.RecipeID) AS row_Num
FROM Recipes AS t1
WHERE RecipeID = 'FB3XSAXRWD'
ORDER BY Name";
cmd.Parameters.AddWithValue("#recipeId", id);
idx = Convert.ToInt32(cmd.ExecuteScalar());
Here is a way to get the row number in Sqlite:
SELECT CategoryID,
Name,
(SELECT COUNT(*)
FROM mytable AS t2
WHERE t2.Name <= t1.Name) AS row_Num
FROM mytable AS t1
ORDER BY Name, CategoryID;
Here's a funny trick you can use in Spatialite to get the order of values. If you use the count() function with a WHERE clause limiting to only values >= the current value, then the count will actually give the order. So if I have a point layer called "mypoints" with columns "value" and "val_order" then:
SELECT value, (
SELECT count(*) FROM mypoints AS my
WHERE my.value>=mypoints.value) AS val_order
FROM mypoints
ORDER BY value DESC;
Gives the descending order of the values.
I can update the "val_order" column this way:
UPDATE mypoints SET val_order = (
SELECT count(*) FROM mypoints AS my
WHERE my.value>=mypoints.value
);
What you are asking can be explained in two different ways, but I'm assuming you want to sort the resulting table and then number those rows according to the sort.
declare #resultrow int
select
#resultrow = row_number() OVER (ORDER BY Name Asc) as 'Row Number'
from Categories WHERE CategoryID = 'C_L776102U'
select #resultrow