Firebird and SQL Server delete highest rows in groups - sql

I need to delete the 3rd and greater records, sorted by product, type and display order, from the following tables:
Stockist table:
CREATE TABLE INVENTORYWEBSTOCKISTS
(
STOCKISTID NUMERIC(10,0) NOT NULL,
PRODUCTID NUMERIC(10,0) NOT NULL,
DISPLAYORDER NUMERIC(10,0),
CUSTOMERID NUMERIC(10,0),
CONSTRAINT PK_INVENTORYWEBSTOCKISTS PRIMARY KEY (STOCKISTID)
);
STOCKISTID is the unique autoinc column for the table and can be used as an ID. PRODUCTID and CUSTOMERID both reference other tables.
Customer table:
CREATE TABLE CUSTOMERS
(
CUSTOMERID NUMERIC(10,0) NOT NULL,
WEBSTOCKISTSTOCKISTTYPE VARCHAR(100),
CONSTRAINT PK_CUSTOMERS PRIMARY KEY (CUSTOMERID)
);
Customers table has records:
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (1, 'Reseller');
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (2, 'Reseller');
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (3, 'Reseller');
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (4, 'Installer');
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (5, 'Installer');
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (6, 'Installer');
insert into CUSTOMERS (CUSTOMERID, WEBSTOCKISTSTOCKISTTYPE) values (7, 'Installer');
Stockist table has records:
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (1, 1, -100, 1);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (2, 1, -101, 2);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (3, 1, -102, 3);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (4, 1, -103, 4);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (5, 1, -104, 5);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (6, 1, -105, 6);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (7, 1, -106, 7);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (10, 2, -107, 3);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (13, 2, -108, 6);
insert into INVENTORYWEBSTOCKISTS (STOCKISTID, PRODUCTID, DISPLAYORDER, CUSTOMERID) values (14, 2, -109, 7);
For the combined results:
select
INVENTORYWEBSTOCKISTS.STOCKISTID,
INVENTORYWEBSTOCKISTS.PRODUCTID,
INVENTORYWEBSTOCKISTS.DISPLAYORDER,
CUSTOMERS.WEBSTOCKISTSTOCKISTTYPE
from
INVENTORYWEBSTOCKISTS
left join
CUSTOMERS on CUSTOMERS.CUSTOMERID = INVENTORYWEBSTOCKISTS.CUSTOMERID
order by
INVENTORYWEBSTOCKISTS.PRODUCTID,
CUSTOMERS.WEBSTOCKISTSTOCKISTTYPE,
INVENTORYWEBSTOCKISTS.DISPLAYORDER
Output:
STOCKISTID, PRODUCTID, DISPLAYORDER, WEBSTOCKISTSTOCKISTTYPE
7, 1, -106, Installer
6, 1, -105, Installer
5, 1, -104, Installer
4, 1, -103, Installer
3, 1, -102, Reseller
2, 1, -101, Reseller
1, 1, -100, Reseller
14, 2, -109, Installer
13, 2, -108, Installer
10, 2, -107, Reseller
I need to delete the 3rd and above record for each product/stockist type group ordered by display order, so would expect it to delete rows 5, 4, and 1.
I have tried heaps of different queries from here and the web generally, I can't find anything that will group and order for the delete and work on both MS SQL and Firebird 1.5.
SQL Fiddle to experiment with: http://sqlfiddle.com/#!3/7101de/1
So after deleting, the table should look like:
Output:
STOCKISTID, PRODUCTID, DISPLAYORDER, WEBSTOCKISTSTOCKISTTYPE
7, 1, -106, Installer
6, 1, -105, Installer
3, 1, -102, Reseller
2, 1, -101, Reseller
14, 2, -109, Installer
13, 2, -108, Installer
10, 2, -107, Reseller
The delete should be executed as one query, so I can pass it to the server.
*** Edit:
Maybe to simplify, if I can do a select command that returns the STOCKISTID's to be deleted, I can then execute the delete commands seperately.
*** Edit 2:
As a test, I added the stockist type field to the INVENTORYWEBSTOCKISTS table, and can run this query:
SELECT INVENTORYWEBSTOCKISTS.STOCKISTID
FROM INVENTORYWEBSTOCKISTS IWS1
WHERE
(SELECT COUNT(*)
FROM INVENTORYWEBSTOCKISTS IWS2
WHERE IWS2.PRODUCTID = IWS1.PRODUCTID
AND IWS2.STOCKISTTYPE = IWS1.STOCKISTTYPE
AND IWS2.DISPLAYORDER <= IWS1.DISPLAYORDER) > 2
and return the correct fields (ie the ones that should be deleted). This was based on a question asked previously on SO.
As soon as I try and link in the CUSTOMER table as the query should be, it gives different results, less rows. Maybe someone might be able to help with that?

Try this, I have used ROW_NUMBER to eliminate your case (From what I understood from your question)
;WITH CTE AS(
SELECT ROW_NUMBER()OVER(PARTITION BY PRODUCTID,WEBSTOCKISTSTOCKISTTYPE
ORDER BY (SELECT 1) )SNO, INVENTORYWEBSTOCKISTS.*,
CUSTOMERS.WEBSTOCKISTSTOCKISTTYPE FROM
INVENTORYWEBSTOCKISTS
LEFT JOIN
CUSTOMERS ON INVENTORYWEBSTOCKISTS.CUSTOMERID = CUSTOMERS.CUSTOMERID
)
SELECT * FROM CTE WHERE SNO <3
Edit: From your comments:
;WITH CTE AS(
SELECT ROW_NUMBER()OVER(PARTITION BY PRODUCTID,WEBSTOCKISTSTOCKISTTYPE
ORDER BY (SELECT DISPLAYORDER) )SNO, INVENTORYWEBSTOCKISTS.*,
CUSTOMERS.WEBSTOCKISTSTOCKISTTYPE FROM
INVENTORYWEBSTOCKISTS
LEFT JOIN
CUSTOMERS ON INVENTORYWEBSTOCKISTS.CUSTOMERID = CUSTOMERS.CUSTOMERID
)
SELECT STOCKISTID FROM CTE WHERE SNO>2
If it is a problem with CTE
DELETE FROM INVENTORYWEBSTOCKISTS WHERE STOCKISTID IN (
SELECT STOCKISTID FROM (
SELECT ROW_NUMBER()OVER(PARTITION BY PRODUCTID,WEBSTOCKISTSTOCKISTTYPE
ORDER BY (SELECT DISPLAYORDER) )SNO, INVENTORYWEBSTOCKISTS.*,
CUSTOMERS.WEBSTOCKISTSTOCKISTTYPE FROM
INVENTORYWEBSTOCKISTS
LEFT JOIN
CUSTOMERS ON INVENTORYWEBSTOCKISTS.CUSTOMERID = CUSTOMERS.CUSTOMERID
) AS A WHERE SNO>2
)

Related

Why it is showing No such column

/* Triggers*/
create table acustomer( id integer primary key, desc text, last_order_id integer);
create table bcustomer ( id integer primary key, item_id int, customer_id int, quan int, price int);
insert into acustomer (desc) values ( 'rohan');
insert into acustomer (desc) values ('mohan');
insert into acustomer (desc) values ('sohan');
select * from acustomer;
create trigger ccustomer after insert on bcustomer
begin
update acustomer set last_order_id = NEW.id where acustomer.id = NEW.customer_id;
end;
insert into bcustomer (item_id, customer_id, quan, price) values (1, 2, 3, 4);
insert into bcustomer (item_id, customer_id, quan, price) values (5, 6, 7, 8);
insert into bcustomer (item_id, customer_id, quan, price) values (8, 9, 10, 20);
insert into bcustomer (item_id, customer_id, quan, price) values (4, 12, 19, 13);
select * from acustomer;
select * from bcustomer;
#On executing insertion in table ccustomer, it is showing error : no such column : NEW.customer.id
Your last insert for bcustomer table is
insert into bcustomer (item_id, customer_id, quan, price) values (4, 12, 19, 13);
which doesn't have any matching last_order_id in acustomer.
If you will run
insert into bcustomer (item_id, customer_id, quan, price) values (4, 1, 19, 13);
customer_id = 1, it will update your acustomer table since it has a matching id, which is 1.
try this dbfiddle.

Find all records in a table where the most recent record in a join table has a column which is not equal to one is this table?

Image the tables:
CREATE TABLE items (
id INT,
price INT
);
CREATE TABLE item_price_history (
id INT,
item_id INT,
price INT
);
With the following data:
INSERT INTO items (id, price) VALUES (1, 199);
INSERT INTO items (id, price) VALUES (2, 159);
INSERT INTO items (id, price) VALUES (3, 129);
INSERT INTO items (id, price) VALUES (4, 119);
INSERT INTO item_price_history (id, item_id, price) VALUES (1, 1, 249);
INSERT INTO item_price_history (id, item_id, price) VALUES (2, 1, 239);
INSERT INTO item_price_history (id, item_id, price) VALUES (3, 1, 229);
INSERT INTO item_price_history (id, item_id, price) VALUES (4, 1, 199);
INSERT INTO item_price_history (id, item_id, price) VALUES (5, 2, 299);
INSERT INTO item_price_history (id, item_id, price) VALUES (6, 2, 259);
INSERT INTO item_price_history (id, item_id, price) VALUES (7, 2, 159);
INSERT INTO item_price_history (id, item_id, price) VALUES (8, 2, 109);
INSERT INTO item_price_history (id, item_id, price) VALUES (9, 3, 129);
INSERT INTO item_price_history (id, item_id, price) VALUES (10, 4, 159);
INSERT INTO item_price_history (id, item_id, price) VALUES (11, 4, 119);
INSERT INTO item_price_history (id, item_id, price) VALUES (13, 4, 99);
Now I would like to find all the items for which the items.price is not equal to the most recent item_price_history.price (id DESC) in item_price_history. In this case that should produce item with id 3 and 4. As their prices does NOT match the most recent price in item_price_history.
I did the following which works:
SELECT items.id, items.price i_price, item_price_history.id, item_price_history.price as ih_price
FROM items
LEFT JOIN item_price_history ON item_price_history.item_id = items.id
AND item_price_history.id = (SELECT MAX(id) FROM item_price_history WHERE item_id = items.id)
WHERE items.price != item_price_history.price
LIMIT 100
However I have a table of about 2 million rows in items and 20 million rows in item_price_history which I need to scan through, so performance is important.
How would I write such a query in a more performant way e.g by using DISTINCT ON or something?
https://www.db-fiddle.com/f/wPkdHvvzS2tmZq2vKUwgNi/2
Here is a query to do it in bulk with DISTINCT ON:
SELECT items.id, items.price i_price, item_price_history.id, item_price_history.price as ih_price
from items
join (select distinct on (item_id) id, item_id, price from item_price_history order by item_id desc, id desc) item_price_history
on item_price_history.item_id = items.id
WHERE items.price != item_price_history.price;
It should be well supported by an index on item_price_history (item_id , id, price); which will let it do a presorted index-only scan.

SQL Server : SELECT query to get DISTINCT and MAX display order value

I have a product table, Category table, and Mapping table. Category saved as a category tree. If a single product has mapped with the last category in a hierarchy of level three. All the levels saved in the mapping table with the same product id.
eg : Assume there is category tre like this Electronic>LapTops>DELL and when product id = 1 assigned to category 'DELL' mapping will save as [1,Electronic],[1,LapTops],[1,DELL]
When I get data with a select query all the category levels appear with the same product Id.
My problem is I need to retrieve data as [productId, ProductName, LastCategortLevel, CategoryName, CategoryId].
Refer actual result below. I just need to pick the highlighted product with the last category level which is the highest category order level.
I can't use another stored procedure or function because it's a small part of a large stored procedure.
The actual database tables are very big. But I have tried to implement the same scenario with small temp tables. see the below queries.
DECLARE #Products TABLE (ProductId INT NOT NULL)
INSERT INTO #Products(ProductId)
SELECT ProductId
FROM (VALUES (1), (2), (3), (4)) as x (ProductId)
DECLARE #Categories TABLE (CategoId INT NOT NULL,
Name VARCHAR(MAX) NOT NULL,
ParentCategoryId INT NOT NULL,
DisplayOrder INT NOT NULL)
-- 1st category tree
INSERT INTO #Categories VALUES (10, 'Electronic', 0, 1)
INSERT INTO #Categories VALUES (11, 'LapTops', 10, 2)
INSERT INTO #Categories VALUES (12, 'DELL', 11, 3)
INSERT INTO #Categories VALUES (13, 'HP', 11, 3)
-- 2st category tree
INSERT INTO #Categories VALUES (14, 'Clothes', 0, 1)
INSERT INTO #Categories VALUES (15, 'T-Shirts', 14, 2)
INSERT INTO #Categories VALUES (16, 'Red', 15, 3)
INSERT INTO #Categories VALUES (17, 'Denim', 14, 2)
INSERT INTO #Categories VALUES (18, 'Levise', 17, 3)
DECLARE #Product_Category_Mappings TABLE(MappingId INT NOT NULL,
ProductId INT NOT NULL,
CategoryId INT NOT NULL)
INSERT INTO #Product_Category_Mappings VALUES (100, 1, 10)
INSERT INTO #Product_Category_Mappings VALUES (101, 1, 11)
INSERT INTO #Product_Category_Mappings VALUES (102, 1, 12)
INSERT INTO #Product_Category_Mappings VALUES (103, 2, 10)
INSERT INTO #Product_Category_Mappings VALUES (104, 2, 11)
INSERT INTO #Product_Category_Mappings VALUES (105, 2, 12)
INSERT INTO #Product_Category_Mappings VALUES (106, 3, 14)
INSERT INTO #Product_Category_Mappings VALUES (107, 3, 15)
INSERT INTO #Product_Category_Mappings VALUES (108, 3, 16)
INSERT INTO #Product_Category_Mappings VALUES (109, 4, 14)
INSERT INTO #Product_Category_Mappings VALUES (110, 4, 17)
INSERT INTO #Product_Category_Mappings VALUES (111, 4, 18)
SELECT *
FROM #Products P
INNER JOIN #Product_Category_Mappings M ON M.ProductId = P.ProductId
INNER JOIN #Categories C ON C.CategoId = M.CategoryId
WHERE M.ProductId = P.ProductId
ORDER BY P.ProductId, C.DisplayOrder
Result of the above script. How I get highlighted rows?
For each ProductId, you want the row with highest DisplayOrder. You can use window functions:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY P.ProductId ORDER BY C.DisplayOrder DESC) rn
FROM #Products P
INNER JOIN #Product_Category_Mappings M ON M.ProductId = P.ProductId
INNER JOIN #Categories C ON C.CategoId = M.CategoryId
WHERE M.ProductId = P.ProductId
) t
WHERE rn = 1
ORDER BY P.ProductId, C.DisplayOrder

COUNT() with GROUP BY - Query to get number of times a set of values appear in a column SQL

Just struggling with this question.
Write a query that selects the following details about the sides that have been ordered in orders:
• The side ID number and side name.
• How many orders the side has been ordered in (regardless of quantity).
I have created a view already and this is the table for it (ordered_sides_details is the name of the view)
View Table
I've wrttien this query but I believe it just counts the number of rows instead of how many times each side is ordered.
SELECT ordered_sides_details.side_name, COUNT(*)
FROM ordered_sides_details
GROUP BY ordered_sides_details.side_name;
This is the resulting table
Obviously its incorrect as 1.25L Coke has only been in 1 order.
Any help with solving this would be awesome. Thanks.
Solution
There must be something wrong with view you've created.
This should be enough to yield proper results:
SELECT
side_id
,side_name
,COUNT(*) AS total_count
FROM dbo.orders
GROUP BY side_id, side_name
Boostrapping (SQL Server)
Scripts for bootstrapping your example:
IF NOT EXISTS (SELECT 1 FROM sys.tables t WHERE t.object_id = OBJECT_ID('dbo.orders'))
BEGIN
CREATE TABLE orders
(
order_id INT,
side_id INT NOT NULL,
side_name NVARCHAR(100) NOT NULL,
ordered_quantity INT NOT NULL,
total_cost MONEY NOT NULL
);
END;
INSERT INTO orders (order_id, side_id, side_name, ordered_quantity, total_cost)
VALUES
(10, 1, '390ml Coke', 1, 3.00),
(5, 2, '1.25l Coke', 2, 10.00),
(8, 3, 'Lava Cake', 3, 8.85),
(7, 4, 'Chicken Wings', 4, 14.00),
(6, 5, 'Garlic Bread', 4, 7.80),
(5, 6, 'Healthy Kale Chips', 3, 16.50),
(5, 6, 'Healthy Kale Chips', 2, 11.00),
(4, 5, 'Garlic Bread', 1, 1.95),
(3, 4, 'Chicken Wings', 1, 3.50),
(2, 3, 'Lava Cake', 2, 5.90);

sql question about ordering after a sum

i have a products table that contains all the sales as in quantity made at a time .. so the table is :
id | product_department_id | product_id | quantity_sold
i need to list for all the product_department_ids the best 2 selling products . Any ideas how i can do so ?
if you can do it in pl/sql it would be great but sql is ok also !
Thanks !
drop table quantity;
create table quantity (
id number primary key,
product_department_id number,
product_id number,
quantity_sold number,
unique (product_department_id, product_id)
);
insert into quantity values (1, 1, 1, 10);
insert into quantity values (2, 1, 2, 20);
insert into quantity values (3, 1, 3, 30);
insert into quantity values (4, 2, 1, 60);
insert into quantity values (5, 2, 2, 50);
insert into quantity values (6, 2, 3, 40);
select * from (
select quantity_sold, product_id, product_department_id,
row_number() over (partition by product_department_id order by quantity_sold desc) r
from quantity
) where r < 3;
Edit Still not sure about what exactly was asked, but if the combination prodcut/department can have multple entries then it would be:
drop table quantity;
create table quantity (
id number primary key,
product_department_id number,
product_id number,
quantity_sold number
);
insert into quantity values ( 1, 1, 1, 15);
insert into quantity values ( 2, 1, 1, 15);
insert into quantity values ( 3, 1, 1, 15);
insert into quantity values ( 4, 1, 2, 20);
insert into quantity values ( 5, 1, 3, 30);
insert into quantity values (10, 2, 1, 60);
insert into quantity values (11, 2, 2, 50);
insert into quantity values (12, 2, 3, 40);
insert into quantity values (13, 2, 3, 30);
select * from (
select sum(quantity_sold),
product_id, product_department_id,
row_number() over (partition by product_department_id
order by sum(quantity_sold) desc
) r
from quantity
group by product_department_id, product_id
) where r < 3
order by product_department_id, product_id;
If a product can have only one department, you can simply order by:
select product_department_id
from YourTable
where rownum < 3
order by
quantity_sold desc