Update OrderID based on the Select Order By Results? - sql

I am using SQLite 3. I have a table MyTable, as follows:
Create table mytable (ID as INTEGER, OrderID as INTEGER, a as INTGER, b as INTEGER);
Insert into mytable (ID, OrderID,a,b) values (1, 1,1,1);
Insert into mytable (ID, OrderID,a,b) values (1, 2,1,2);
Insert into mytable (ID, OrderID,a,b) values (2, 1,1,3);
Insert into mytable (ID, OrderID,a,b) values (2, 3,2,1);
Insert into mytable (ID, OrderID,a,b) values (3, 1,2,3);
Now if I using the following statement:
Select * from mytable ORDER BY a desc, b desc;
I will get all rows in a different order, as follows:
(3, 1, 2, 3);
(2, 3, 2, 1);
(2, 1, 1, 3);
(1, 2, 1, 2);
(1, 1, 1, 1);
Now I want to update the order ID to the sequence number of the rows appear in the above results, as follows:
(3, 1, 2, 3);
(2, 2, 2, 1);
(2, 3, 1, 3);
(1, 4, 1, 2);
(1, 5, 1, 1);
How to do so?

Try This :-
Select ROW_NUMBER() OVER (ORDER BY (SELECT 1 ) ) AS ordNo, * INTO #TempTable from
mytable ORDER BY a desc, b desc;
UPDATE mytable SET OrderID = ordNo FROM #TempTable WHERE mytable.ID
=#TempTable.ID AND mytable.a=#TempTable.a AND mytable.b=#TempTable.b

Assuming that the a and b values are unique:
update mytable t
set orderid = (select count(*)
from mytable t2
where t2.a > t.a or
(t2.a = t.a and t2.b >= t.b)
);

There is a column [rowid][1] responsible for every rowid
Most tables in a typical SQLite database schema are rowid tables.
Rowid tables are distinguished by the fact that they all have a unique, non-NULL, signed 64-bit integer rowid that is used as the access key for the data in the underlying B-tree storage engine.
Due to old SQLite version didn't support ROW_NUMBER window function, you can use a subquery in select to make it.
You can try to use correlated subquery and UPDATE by rowid.
Schema (SQLite v3.18)
Create table mytable (ID INT, OrderID INT, a INT, b INT);
Insert into mytable (ID, OrderID,a,b) values (1, 1,1,1);
Insert into mytable (ID, OrderID,a,b) values (1, 2,1,2);
Insert into mytable (ID, OrderID,a,b) values (2, 1,1,3);
Insert into mytable (ID, OrderID,a,b) values (2, 3,2,1);
Insert into mytable (ID, OrderID,a,b) values (3, 1,2,3);
update mytable
set orderid=
(
SELECT (select count(*)
from mytable tt
where tt.a > t1.a or
(tt.a = t1.a and tt.b >= t1.b)) rn
FROM mytable t1
where mytable.rowid=t1.rowid
);
Query #1
SELECT * FROM mytable order by OrderID;
| ID | OrderID | a | b |
| --- | ------- | --- | --- |
| 3 | 1 | 2 | 3 |
| 2 | 2 | 2 | 1 |
| 2 | 3 | 1 | 3 |
| 1 | 4 | 1 | 2 |
| 1 | 5 | 1 | 1 |
View on DB Fiddle

Related

Treat NULL as largest possible value

I want to get the row with maximum Transaction number Grouped on the basis of Code.
CREATE TABLE SaleOrder
(
TransactionNo Int,
SaleOrderDate DATE,
Code VARCHAR(25),
Quantity INT,
TotalAmount Numeric(18,2),
Remarks VARCHAR(25)
)
INSERT INTO SaleOrder VALUES (NULL, '2018-10-01', 'SO-001-OCT-18', 6, 2500, 'Hello');
INSERT INTO SaleOrder VALUES (1, '2018-10-01', 'SO-001-OCT-18', 8, 2600, 'Hello');
INSERT INTO SaleOrder VALUES (2, '2018-10-01', 'SO-001-OCT-18', 12, 3400, 'Hello');
INSERT INTO SaleOrder VALUES (3, '2018-10-01', 'SO-001-OCT-18', 9, 2900, 'Hello');
INSERT INTO SaleOrder VALUES (4, '2018-10-01', 'SO-001-OCT-18', 2, 900, 'Hello');
INSERT INTO SaleOrder VALUES (NULL, '2018-10-01', 'SO-002-OCT-18', 6, 2500, 'Hello');
INSERT INTO SaleOrder VALUES (NULL, '2018-10-01', 'SO-003-OCT-18', 6, 2500, 'Hello');
INSERT INTO SaleOrder VALUES (0, '2018-10-01', 'SO-004-OCT-18', 6, 2500, 'Hello');
SELECT * FROM SaleOrder O
WHERE TransactionNo = (SELECT MAX(ISNULL(TransactionNo, 1)) FROM SaleOrder GROUP BY Code)
Here when TransactionNo is NULL it's not returning any record against it while it should return that too.
There is absolutely no reason to treat NULL as largest possible value. You can always use the ROW_NUMBER trick:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Code ORDER BY TransactionNo DESC) AS RN
FROM SaleOrder
)
SELECT * FROM cte
WHERE RN = 1
Result:
| TransactionNo | SaleOrderDate | Code | Quantity | TotalAmount | Remarks | RN |
|---------------|---------------|---------------|----------|-------------|---------|----|
| 4 | 2018-10-01 | SO-001-OCT-18 | 2 | 900.00 | Hello | 1 |
| NULL | 2018-10-01 | SO-002-OCT-18 | 6 | 2500.00 | Hello | 1 |
| NULL | 2018-10-01 | SO-003-OCT-18 | 6 | 2500.00 | Hello | 1 |
| 0 | 2018-10-01 | SO-004-OCT-18 | 6 | 2500.00 | Hello | 1 |
When TransactionNo is NULL and query returns more than one row that could not assigned to the filter
The below may help
SELECT * FROM SaleOrder O
WHERE TransactionNo = (SELECT TOP 1 MAX(ISNULL(NULL, 1)) FROM SaleOrder GROUP BY Code)
Note that this could take any record with TransactionNo having a NULL value.
Segregating the logic for TransactionNo filter would be easier to extend and maintain. Example below:
DECLARE #TransactionNo int
SELECT TOP 1 #TransactionNo = MAX(ISNULL(TransactionNo, 1)) FROM SaleOrder GROUP BY Code -- (OR) Logic here
SELECT * FROM SaleOrder O
WHERE TransactionNo = #TransactionNo
when you use = select in your where clause its totally wrong because this is possible you have multiple records,so you have to change your code like this:
SELECT MAX(ISNULL(TransactionNo, 1)),code FROM SaleOrder O
GROUP BY Code
but if you want to return only one record you can use it like this:
SELECT * FROM SaleOrder O
WHERE TransactionNo = (SELECT TOP 1 MAX(ISNULL(NULL, 1)) FROM SaleOrder GROUP BY Code)
I think this ISNULL check should solve your problem and replace = with IN subquery can return multiple records
WHERE ISNULL(TransactionNo, 1) IN
Try this:
select TransactionNo,SaleOrderDate,Code,Quantity,TotalAmount,Remarks from (
select TransactionNo,SaleOrderDate,Code,Quantity,TotalAmount,Remarks,
row_number() over (partition by code order by transactionno desc) rn
from (
select TransactionNo,SaleOrderDate,Code,Quantity,TotalAmount,Remarks,
coalesce(transactionno, count(*) over (partition by code) + 1) transactionno2
from SaleOrder
) a
) a where rn = 1
Explanation:
With this line coalesce(transactionno, count(*) over (partition by code) + 1) transactionno2 I assign greatest value per group (partitioned by code) where it's null. But be aware when you have two NULLs, rows will be tied in this case and it would be non-deterministic.
Below code will give you much more info than you requested, you can play with it, add some comment if have any questions.
CREATE TABLE #SaleOrder
(
TransactionNo Int,
#SaleOrderDate DATE,
Code VARCHAR(25),
Quantity INT,
TotalAmount Numeric(18,2),
Remarks VARCHAR(25)
)
INSERT INTO #SaleOrder VALUES (NULL, '2018-10-01', 'SO-001-OCT-18', 6, '2500', 'Hello');
INSERT INTO #SaleOrder VALUES (1, '2018-10-01', 'SO-001-OCT-18', 8, '2600', 'Hello');
INSERT INTO #SaleOrder VALUES (2, '2018-10-01', 'SO-001-OCT-18', 12, '3400', 'Hello');
INSERT INTO #SaleOrder VALUES (3, '2018-10-01', 'SO-001-OCT-18', 9, '2900', 'Hello');
INSERT INTO #SaleOrder VALUES (4, '2018-10-01', 'SO-001-OCT-18', 2, '900', 'Hello');
INSERT INTO #SaleOrder VALUES (NULL, '2018-10-01', 'SO-002-OCT-18', 6, '2500', 'Hello');
INSERT INTO #SaleOrder VALUES (NULL, '2018-10-01', 'SO-003-OCT-18', 6, '2500', 'Hello');
INSERT INTO #SaleOrder VALUES (0, '2018-10-01', 'SO-004-OCT-18', 6, '2500', 'Hello');
-- final select
SELECT top 1 -- optional, if you want to return 1 record
Code,
sum(Quantity) as totalQuantity,
sum(TotalAmount) as totallAmount,
count(1) as totalOrdersPerCode
FROM #SaleOrder O
group by Code
order by count(1) desc
-- drop temp table
drop table #SaleOrder

How can i remove rows from several tables with SQL?

I have a 3 tables:
mainTable:
id | uniqField
--------------
1 | 1111
2 | 1111
3 | 2222
4 | 2222
5 | 3333
6 | 4444
table1:
id|name|deleted
----------
1|Mike|0
2|Mike|0
5|John|0
table2:
id|name|deleted
----------
3|Peke|0
4|Peke|0
6|Vels|0
That tables bind by id field.
Now i want to remove duplicates from this tables. For mainTable i can use:
DELETE mainTable
FROM mainTable
LEFT OUTER JOIN (
SELECT MIN(id) as RowId, uniqField
FROM mainTable
GROUP BY uniqField
) as KeepRows ON
mainTable.id= KeepRows.RowId
WHERE
KeepRows.RowId IS NULL
But in tables1 and table2 i want to set deleted field to 0->1 to duplicates I mean:
1 | 1111
2 | 1111 -> duplicate->remove
1|Mike|0
2|Mike|0->duplicate-> 0->1
Try this one -
SET NOCOUNT ON;
DECLARE #mainTable TABLE
(
id INT,
uniqField INT
)
INSERT INTO #mainTable (id, uniqField)
VALUES (1, 1111), (2, 1111), (3, 2222), (4, 2222), (5, 3333), (6, 4444)
DECLARE #deleted TABLE (id INT)
;WITH cte AS
(
SELECT *, RowNum = ROW_NUMBER() OVER (PARTITION BY uniqField ORDER BY 1/0)
FROM #mainTable
)
DELETE FROM cte
OUTPUT DELETED.id INTO #deleted
WHERE RowNum > 1
DECLARE #table1 TABLE
(
id INT,
uniqField VARCHAR(10),
deleted BIT
)
DECLARE #table2 TABLE
(
id INT,
uniqField VARCHAR(10),
deleted BIT
)
INSERT INTO #table1 (id, uniqField, deleted)
VALUES (1, 'Mike', 0), (2, 'Mike', 0), (5, 'John', 0)
INSERT INTO #table2 (id, uniqField, deleted)
VALUES (3, 'Peke', 0), (4, 'Peke', 0), (6, 'Vels', 0)
UPDATE #table1
SET deleted = 1
FROM #deleted
WHERE [#deleted].id = [#table1].id
UPDATE #table2
SET deleted = 1
FROM #deleted
WHERE [#deleted].id = [#table2].id
SELECT * FROM #table1
SELECT * FROM #table2
Output -
id uniqField deleted
----------- ---------- -------
1 Mike 0
2 Mike 1
5 John 0
id uniqField deleted
----------- ---------- -------
3 Peke 0
4 Peke 1
6 Vels 0

SQL : how to find leaf rows?

i have a self related table myTable like :
ID | RefID
----------
1 | NULL
2 | 1
3 | 2
4 | NULL
5 | 2
6 | 5
7 | 5
8 | NULL
9 | 7
i need to get leaf rows on any depth
based on the table above, the result must be :
ID | RefID
----------
3 | 2
4 | NULL
6 | 5
8 | NULL
9 | 7
thank you
PS: the depth may vary , here is very small example
Try:
SELECT id,
refid
FROM mytable t
WHERE NOT EXISTS (SELECT 1
FROM mytable
WHERE refid = t.id)
DECLARE #t TABLE (id int NOT NULL, RefID int NULL);
INSERT #t VALUES (1, NULL), (2, 1), (3, 2), (5, NULL),
(6, 5), (4, NULL), (7, 5), (8, NULL), (9, 8), (10, 7);
WITH CTE AS
(
-- top level
SELECT id, RefID, id AS RootId, 0 AS CTELevel FROM #t WHERE REfID IS NULL
UNION ALL
SELECT T.id, T.RefID, RootId, CTELevel + 1 FROM #t T JOIN CTE ON T.RefID = CTE.id
), Leafs AS
(
SELECT
id, RefID, DENSE_RANK() OVER (PARTITION BY CTE.RootId ORDER BY CTELevel DESC) AS Rn
FROM CTE
)
SELECT
id, RefID
FROM
Leafs
WHERE
rn = 1
select ID, RefId
from myTable t1 left join myTable t2 on t1.ID = t2.RefID
where t2.RefID is null
try this:
SELECT *
FROM
my_table
WHERE
id NOT IN
(
SELECT DISTINCT
refId
FROM
my_table
WHERE
refId IS NOT NULL
)

How to update X% of rows to A, Y% of rows to B, Z% of rows to C

I have a table like this:
Products
(
ID int not null primary key,
Type int not null,
Route varchar(20) null
)
I have a list on the client in this format:
Type=1, Percent=0.4, Route=A
Type=1, Percent=0.4, Route=B
Type=1, Percent=0.2, Route=C
Type=2, Percent=0.5, Route=A
Type=2, Percent=0.5, Route=B
Type=3, Percent=1.0, Route=C
...etc
When done, I'd like to assign 40% of type 1 products to Route A, 40% to Route B and 20% to Route C. Then 50% of type 2 products to Route A and 50% of type 2 products to Route B, etc.
Is there some way to do this in a single update statement?
If not in one giant statement, can it be done in one statement per type or one statement per route? As currently we're doing one per type+route any of the above would be an improvement.
Here's an Oracle statement that I prepared before you posted that you were using SQL-Server, but it might give you some ideas, though you will have to roll your own ratio_to_report analytic function using CTE and self-joins. We calculate the cumulative proportion of each type in the products and client route tables and do a non equi-join on the matching proportion bands. The sample data I have used has some round-offs but these will reduce for larger data sets.
Here's the setup:
create table products (id int not null primary key, "type" int not null, route varchar (20) null);
create table clienttable ( "type" int not null, percent number (10, 2) not null, route varchar (20) not null);
insert into clienttable ("type", percent, route) values (1, 0.4, 'A');
insert into clienttable ("type", percent, route) values (1, 0.4, 'B');
insert into clienttable ("type", percent, route) values (1, 0.2, 'C');
insert into clienttable ("type", percent, route) values (2, 0.5, 'A');
insert into clienttable ("type", percent, route) values (2, 0.5, 'B');
insert into clienttable ("type", percent, route) values (3, 1.0, 'C');
insert into products (id, "type", route) values (1, 1, null);
insert into products (id, "type", route) values (2, 1, null);
insert into products (id, "type", route) values (3, 1, null);
insert into products (id, "type", route) values (4, 1, null);
insert into products (id, "type", route) values (5, 1, null);
insert into products (id, "type", route) values (6, 1, null);
insert into products (id, "type", route) values (7, 1, null);
-- 7 rows for product type 1 so we will expect 3 of route A, 3 of route B, 1 of route C (rounded)
insert into products (id, "type", route) values (8, 2, null);
insert into products (id, "type", route) values (9, 2, null);
insert into products (id, "type", route) values (10, 2, null);
insert into products (id, "type", route) values (11, 2, null);
insert into products (id, "type", route) values (12, 2, null);
-- 5 rows for product type 2 so we will expect 3 of route A and 2 of route B (rounded)
insert into products (id, "type", route) values (13, 3, null);
insert into products (id, "type", route) values (14, 3, null);
-- 2 rows for product type 3 so we will expect 2 of route C
and here's the statement
select prods.id, prods."type", client.route cr from
(
select
p.id,
p."type",
row_number () over (partition by p."type" order by p.id) / count (*) over (partition by p."type") cum_ratio
from
products p
) prods
inner join
(
select "type", route, nvl (lag (cum_ratio, 1) over (partition by "type" order by route), 0) ratio_start, cum_ratio ratio_end from
(select "type", route, sum (rr) over (partition by "type" order by route) cum_ratio
from (select c."type", c.route, ratio_to_report (c.percent) over (partition by "type") rr from clienttable c))) client
on prods."type" = client."type"
and prods.cum_ratio >= client.ratio_start and prods.cum_ratio < client.ratio_end
This gives the following result:-
+----+------+----+
| ID | type | CR |
+----+------+----+
| 1 | 1 | A |
| 2 | 1 | A |
| 3 | 1 | B |
| 4 | 1 | B |
| 5 | 1 | B |
| 6 | 1 | C |
| 8 | 2 | A |
| 9 | 2 | A |
| 10 | 2 | B |
| 11 | 2 | B |
| 13 | 3 | C |
+----+------+----+
How about something like
--For updating type 1, set every route for type 1 as null.
UPDATE MyTable
SET [Route] = null
WHERE [Type] = '1'
--Update Route A(40%)
DECLARE #myVal int;
SET #myVal =CAST(0.4*(SELECT COUNT(*) FROM myTable WHERE [Type]='1') AS INT);
WITH tab AS
(
SELECT TOP (#myVal) *
FROM myTable
)
UPDATE tab
SET [Route] = 'A'
WHERE [Route] is null
--Update Route B (40%)
DECLARE #myVal int;
SET #myVal =CAST(0.4*(SELECT COUNT(*) FROM myTable WHERE [Type]='1') AS INT);
WITH tab AS
(
SELECT TOP (#myVal) *
FROM myTable
)
UPDATE tab
SET [Route] = 'B'
WHERE [Route] is null
--Update Route C (20%)
DECLARE #myVal int;
SET #myVal =CAST(0.2*(SELECT COUNT(*) FROM myTable WHERE [Type]='1') AS INT);
WITH tab AS
(
SELECT TOP (#myVal) *
FROM myTable
)
UPDATE tab
SET [Route] = 'C'
WHERE [Route] is null
I do not know if similar functionality exist in SQL Server. In Oracle there is SAMPLE clause.
Below query selects 10% of rows from a table:
SELECT empno
FROM scott.emp
SAMPLE (10)
/
Then your update would be easy... Maybe smth similar exists in SQL Server. You can also count rows or data then calc percent then update...
WITH po AS
( SELECT
ID,
Type,
ROW_NUMBER() OVER ( PARTITION BY Type
ORDER BY ID
) AS Rn,
COUNT(*) OVER (PARTITION BY Type) AS CntType
FROM
Products
)
, ro AS
( SELECT
Type,
Route,
( SELECT SUM(rr.Percent)
FROM Route AS rr
WHERE rr.Type = r.Type
AND rr.Route <= r.Route
) AS SumPercent
FROM
Routes AS r
)
UPDATE p
SET p.Route =
( SELECT MIN(ro.Route)
FROM ro
WHERE ro.Type = po.Type
AND ro.SumPercent >= po.Rn / po.CntType
)
FROM Products AS p
JOIN
po ON po.ID = p.ID ;

double sorted selection from a single table

I have a table with an id as the primary key, and a description as another field.
I want to first select the records that have the id<=4, sorted by description, then I want all the other records (id>4), sorted by description. Can't get there!
select id, descr
from t
order by
case when id <= 4 then 0 else 1 end,
descr
select *, id<=4 as low from table order by low, description
You may want to use an id <= 4 expression in your ORDER BY clause:
SELECT * FROM your_table ORDER BY id <= 4 DESC, description;
Test case (using MySQL):
CREATE TABLE your_table (id int, description varchar(50));
INSERT INTO your_table VALUES (1, 'c');
INSERT INTO your_table VALUES (2, 'a');
INSERT INTO your_table VALUES (3, 'z');
INSERT INTO your_table VALUES (4, 'b');
INSERT INTO your_table VALUES (5, 'g');
INSERT INTO your_table VALUES (6, 'o');
INSERT INTO your_table VALUES (7, 'c');
INSERT INTO your_table VALUES (8, 'p');
Result:
+------+-------------+
| id | description |
+------+-------------+
| 2 | a |
| 4 | b |
| 1 | c |
| 3 | z |
| 7 | c |
| 5 | g |
| 6 | o |
| 8 | p |
+------+-------------+
8 rows in set (0.00 sec)
Related post:
Using MySql, can I sort a column but have 0 come last?
select id, description
from MyTable
order by case when id <= 4 then 0 else 1 end, description
You can use UNION
SELECT * FROM (SELECT * FROM table1 WHERE id <=4 ORDER by description)aaa
UNION
SELECT * FROM (SELECT * FROM table1 WHERE id >4 ORDER by description)bbb
OR
SELECT * FROM table1
ORDER BY
CASE WHEN id <=4 THEN 0
ELSE 1
END, description