Select all rows based on alternative publisher - sql

I want list all the rows by alternative publisher with price ascending, see the example table below.
id publisher price
1 ABC 100.00
2 ABC 150.00
3 ABC 105.00
4 XYZ 135.00
5 XYZ 110.00
6 PQR 105.00
7 PQR 125.00
The expected result would be:
id publisher price
1 ABC 100.00
6 PQR 105.00
5 XYZ 110.00
3 ABC 105.00
7 PQR 125.00
4 XYZ 135.00
2 ABC 150.00
What would be the required SQL?

This should do it:
select id, publisher, price
from (
select id, publisher, price,
row_number() over (partition by publisher order by price) as rn
from publisher
) t
order by rn, publisher, price
The window functions assigns unique numbers for each publisher price. Based on that the outer order by will then first display all rows with rn = 1 which are the rows for each publisher with the lowest price. The second row for each publisher has the second lowest price and so on.
SQLFiddle example: http://sqlfiddle.com/#!4/06ece/2

SELECT id, publisher, price
FROM tbl
ORDER BY row_number() OVER (PARTITION BY publisher ORDER BY price), publisher;
One cannot use the output of window functions in the WHERE or HAVING BY clauses because window functions are applied after those. But one can use window functions in the ORDER BY clause.
SQL Fiddle.

Not sure what your table name is - I have called it publishertable. But the following will order the result by price in ascending order - which is the result you are looking for:
select id, publisher, price from publishertable order by price asc

if I've got it right. You should use ROW_NUMBER() function to range prices inside of each publisher and then order by this range and publisher.
SELECT ID,
Publisher,
Price,
Row_number() OVER (PARTITION BY Publisher ORDER BY Price) as rn
FROM T
ORDER BY RN,Publisher
SQLFiddle demo

Related

Get the running unique count of items till a give date, similar to running total but instead a running unique count

I have a table with user shopping data as shown below
I want an output similar to running total but instead I want the running total of the count of unique categories that the user has shopped for by date.
I know I have to make use of ROWS PRECEDING AND FOLLOWING in the count function but I am not able to user count(distinct category) in a window function
Dt category userId
4/10/2022 Grocery 123
4/11/2022 Grocery 123
4/12/2022 MISC 123
4/13/2022 SERVICES 123
4/14/2022 RETAIl 123
4/15/2022 TRANSP 123
4/20/2022 GROCERY 123
Desired output
Dt userID number of unique categories
4/10/2022 123 1
4/11/2022 123 1
4/12/2022 123 2
4/13/2022 123 3
4/14/2022 123 4
4/15/2022 123 5
4/20/2022 123 5
Consider below approach
select Dt, userId,
( select count(distinct category)
from t.categories as category
) number_of_unique_categories
from (
select *, array_agg(lower(category)) over(partition by userId order by Dt) categories
from your_table
) t
if applied to sample data in your question - output is

How to find lowest value from one columns that has been Grouped by SQL Server

I'm looking for some assistance: I am looking to get this into a report but not sure how to achieve this.
Here is the data stored in the table:
Product | Quantity | Status | Line
Product1 1 Active 1000
Product2 2 Active 2000
Product1 2 Active 3000
Product1 1 InDev 4000
Product2 2 Active 5000
I am grouping by Product and Status and summing up Quantity.
But looking to also retrieve the lowest line number for row in the group.
My expected result would be like below:
Product | Quantity | Status | Line
Product1 3 Active 1000
Product2 4 Active 2000
Product1 1 InDev 5000
Any help would be greatly appreciated
This can be done if you group by Product, Status and aggregate:
select Product, sum(Quantity) Quantity, Status, min(Line) Line
from tablename
group by Product, Status
You can use window function :
select *
from (select t.*, row_number() over (partition by product, status order by line) as seq,
sum(qty) over (partition by product, status) as sum_qty
from table t
) t
where seq = 1;
If table has only available columns (in question) then you can do aggregation :
select product, sum(qty), status, min(line) as line
from table t
group by product, status
order by line;

Selecting ID's based on multiple subquery

This my my first post to Stack Overflow, I appreciate and will take in any positive criticism to better form any future questions.
Question:
I'm trying to create a Select query where to gather all orders which have only the top 8 items in them.
I'm working with MS-Access 2013.
My current Query, which doesn't work, looks like this.
SELECT OrderID
From DirectOrders
WHERE OrderID <> ANY
(
SELECT OrderID
FROM DirectOrders
WHERE SKU <> ANY
(
SELECT TOP 8 SKU
FROM DirectOrders
GROUP BY SKU
ORDER BY COUNT(SKU) DESC
)
)
The single table that is below.
OrderID Customer SKU Qty
177622 CustomerA 1001 20
177622 CustomerA 1002 2
177624 CustomerB 1001 200
177626 CustomerC 1003 50
177626 CustomerC 1004 150
177630 CustomerC 1005 1000
177632 CustomerA 1006 1
177632 CustomerA 1007 3
177632 CustomerA 1008 9
177632 CustomerA 1009 1
177632 CustomerA 1010 4
177632 CustomerA 1011 3
177634 CustomerC 1012 5
177634 CustomerC 1013 5
177640 CustomerD 1014 4
177642 CustomerA 1015 4
177642 CustomerA 1016 48
177642 CustomerA 1017 15
177644 CustomerB 1018 50
Here was the flow that I was trying to accomplish.
Select Top 8 SKU's by Count
Select All OrderID's that do not have one of those 8 SKU's
Select All OrderID's That are not part of the selected OrderID's in List 2.
I would do this with aggregation:
SELECT do.OrderID
FROM DirectOrders as do LEFT JOIN
(SELECT TOP 8 SKU
FROM DirectOrders
GROUP BY SKU
ORDER BY COUNT(SKU) DESC, SKU
) as s8
ON do.SKU = s8.SKU
GROUP BY do.OrderId
HAVING COUNT(*) = COUNT(s8.SKU);
Notes:
In MS Access, TOP is really TOP WITH TIES. To get exactly 8 values you need a tie breaker. This query uses SKU for that purpose.
The LEFT JOIN determines if there is a match between each item in an order and the top 8 items.
The HAVING clause is saying: The count of rows with items is the same as the count of rows that match one of the top 8. Hence, all are in the order.
I think you need something like this. However, you might get some strange results if you are using count SKU because apart from 1001 the count of the other SKUs is 1. So apart from 1001, all the other SKUs are in the top 8 based on the count(SKU)
SELECT * FROM DirectOrderswhere SKU in
(select top 8 SKU from DirectOrders group by SKU order by count(SKU) desc);
Access's TOP function does not break ties, so instead of reporting just the top 8, it will order the items per your order by and then report enough to cover the top value you put in, and all ties. For example, with your sample data, it will report the same 17 records if you do top 8 or just top 2, since all but one of your SKUs have only 1 order.
If you want to report only the top 8, you should add to the query to make the ordering unique. In this case, I would probably order by COUNT(SKU) DESC, COUNT(QTY) DESC, MAX(ORDERID) desc, SKU So that it prioritizes highest number of orders, highest quantity, and then makes a choice based on the latest OrderID with that sku, and if all else fails, order by the SKU itself. Only the SKU is guaranteed to be unique for each row, but just ordering by SKU might not give the best result if you are looking for the truly relevant "top 8".
SELECT OrderID
From DirectOrders
WHERE OrderID NOT IN
(
SELECT OrderID
FROM DirectOrders
WHERE SKU NOT IN
(
SELECT TOP 8 SKU
FROM DirectOrders
GROUP BY SKU
ORDER BY COUNT(SKU) DESC, SUM(QTY) DESC, MAX(ORDERID) DESC, SKU
)
)

Firebird Query- Return first row each group

In a firebird database with a table "Sales", I need to select the first sale of all customers. See below a sample that show the table and desired result of query.
---------------------------------------
SALES
---------------------------------------
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
3 25 05/04/16 08:10
4 31 07/03/16 10:22
5 22 01/02/16 12:30
6 22 10/01/16 08:45
Result: only first sale, based on sale date.
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
4 31 07/03/16 10:22
6 22 10/01/16 08:45
I've already tested following code "Select first row in each GROUP BY group?", but it did not work.
In Firebird 2.5 you can do this with the following query; this is a minor modification of the second part of the accepted answer of the question you linked to tailored to your schema and requirements:
select x.id,
x.customerid,
x.dthrsale
from sales x
join (select customerid,
min(dthrsale) as first_sale
from sales
group by customerid) p on p.customerid = x.customerid
and p.first_sale = x.dthrsale
order by x.id
The order by is not necessary, I just added it to make it give the order as shown in your question.
With Firebird 3 you can use the window function ROW_NUMBER which is also described in the linked answer. The linked answer incorrectly said the first solution would work on Firebird 2.1 and higher. I have now edited it.
Search for the sales with no earlier sales:
SELECT S1.*
FROM SALES S1
LEFT JOIN SALES S2 ON S2.CUSTOMERID = S1.CUSTOMERID AND S2.DTHRSALE < S1.DTHRSALE
WHERE S2.ID IS NULL
Define an index over (customerid, dthrsale) to make it fast.
in Firebird 3 , get first row foreach customer by min sales_date :
SELECT id, customer_id, total, sales_date
FROM (
SELECT id, customer_id, total, sales_date
, row_number() OVER(PARTITION BY customer_id ORDER BY sales_date ASC ) AS rn
FROM SALES
) sub
WHERE rn = 1;
İf you want to get other related columns, This is where your self-answer fails.
select customer_id , min(sales_date)
, id, total --what about other colums
from SALES
group by customer_id
So simple as:
select CUSTOMERID min(DTHRSALE) from SALES group by CUSTOMERID

Find the the value of one field that matches the maximum value of data in another field

I'm trying to write a query that gets the value of one field that's associated with the maximum value of another field (or fields). Let's say I have the following table of data:
OrderID CustomerID OrderDate LocationID
1 4 1/1/2001 1001
2 4 1/2/2001 1003
3 4 1/3/2001 1001
4 5 1/4/2001 1001
5 5 1/5/2001 1001
6 5 1/6/2001 1003
7 5 1/7/2001 1002
8 5 1/8/2001 1003
9 5 1/8/2001 1002
Grouping by CustomerID, I want to get the maximum OrderDate and then the LocationID associated with whatever is the maximum OrderDate. If there are several records that share the maximum order date, then take the LocationID associated with the maximum OrderID from among those records with the maximum date.
The final set of data should look like this:
CustomerID OrderDate LocationID
4 1/3/2001 1001
5 1/8/2001 1002
I had been trying to write a query with lots of nested subqueries and ugly joins, but I'm not really getting anywhere. What SQL do I need to write to help me get this result.
with cte As
(
select *,
row_number() over (partition by CustomerID
order by OrderDate desc, OrderId desc) as rn
from yourtable
)
select CustomerID, OrderDate,LocationID
from cte
where rn=1;
SELECT
C.Name,
C.CustomerID,
X.*
FROM
Customers C
CROSS APPLY (
SELECT TOP 1 OrderDate, LocationID
FROM Orders O
WHERE C.CustomerID = O.CustomerID
ORDER BY OrderDate Desc, OrderID Desc
) X
If you will pull any columns from the Customers table, this will probably outperform other methods.
If not, then the Row_Number answer, pulling only from Orders, will probably be best. But if you restrict by Customer in any way, then the CROSS APPLY will again be best. Possibly by a big margin.
The trick is to use a subquery as a value, not as a join:
select customerId,orderDate,locationId
from orders o1
where orderDate = (
select top 1 orderdate
from orders o2
where o1.customerId = o2.customerId
order by orderdate desc
)