SQL group by multiple fields get first occurrence - sql

I have this table (sales_lines):
id sale_id sale_seq_id other_fields
----------------------------------------
1 1 1
2 1 2
3 2 1
4 3 1
5 3 2
But this table can have a duplicated sale_seq_id (yes, it's an error). Like this:
id sale_id sale_seq_id other_fields
----------------------------------------
1 1 1
2 1 2
3 1 2
4 2 1
5 3 1
6 3 1
7 3 2
Lines 3 and 6 are errors, so I should discard them.
How can I do it?

To delete the wrong records do
delete from sales_lines
where id not in
(
select min(id)
from sales_lines
group by sale_id, sale_seq_id
)
To just delete the correct data do
select min(id), sale_id, sale_seq_id
from sales_lines
group by sale_id, sale_seq_id

I would use correlated sub-query :
select sl.*
from sales_line sl
where sl.id = (select min(sl1.id)
from sales_line sl1
where sl1.sale_id = sl.sale_id and
sl1.sale_seq_id = sl.sale_seq_id
);
If your DBMS supports window function then you can do :
select sl.*
from (select sl.*,
row_number() over (partition by sl.sale_id, sl.sale_seq_id order by sl.id) as seq
from sales_line sl
) sl
where seq = 1;
By this way, you will get full row with other fields too.

Related

Get max record for each group of records, link multiple tables

I seek to find the maximum timestamp (ob.create_ts) for each group of marketid's (ob.marketid), joining tables obe (ob.orderbookid = obe.orderbookid) and market (ob.marketid = m.marketid). Although there are a number of solutions posted like this for a single table, when I join multiple tables, I get redundant results. Sample table and desired results below:
table: ob
orderbookid
marketid
create_ts
1
1
1664635255298
2
1
1664635255299
3
1
1664635255300
4
2
1664635255301
5
2
1664635255302
6
2
1664635255303
table: obe
orderbookentryid
orderbookid
entryname
1
1
'entry-1'
2
1
'entry-2'
3
1
'entry-3'
4
2
'entry-4'
5
2
'entry-5'
6
3
'entry-6'
7
3
'entry-7'
8
4
'entry-8'
9
5
'entry-9'
10
6
'entry-10'
table: m
marketid
marketname
1
'market-1'
2
'market-2'
desired results
ob.orderbookid
ob.marketid
obe.orderbookentryid
obe.entryname
m.marketname
3
1
6
'entry-6'
'market-1'
3
1
7
'entry-7'
'market-1'
6
2
10
'entry-10'
'market-2'
Use ROW_NUMBER() to get a properly filtered ob table. Then JOIN the other tables onto that!
WITH
ob_filtered AS (
SELECT
orderbookid,
marketid
FROM
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY
marketid
ORDER BY
create_ts DESC
) AS create_ts_rownumber
FROM
ob
) ob_with_rownumber
WHERE
create_ts_rownumber = 1
)
SELECT
ob_filtered.orderbookid,
ob_filtered.marketid,
obe.orderbookentryid,
obe.entryname,
m.marketname
FROM
ob_filtered
JOIN m
ON m.marketid = ob_filtered.marketid
JOIN obe
ON ob_filtered.orderbookid = obe.orderbookid
;

SQL update order in relation ship based on ID

I wonder how to update order in this table for many-to-many relationship using SQL based on PostsId.
So my table now looks like:
I'm using SQL Server
BlogsId
PostsId
Order
1
1
1
0
2
2
1
0
3
3
2
0
3
4
2
0
3
5
3
0
3
6
3
0
but I want to update Order using SQL to this:
BlogsId
PostsId
Order
1
1
1
1
2
2
1
2
3
3
2
1
3
4
2
2
3
5
3
1
3
6
3
2
So for example: Blog with Id 3 is the first blog in Post with Id 2, Blog with Id 4 is the second Blog in Post with Id 2 and etc...
I've tried this:
DECLARE #myVar int
SET #myVar = 0
UPDATE [dbo].[BlogPost]
SET #myVar = [Order] = #myVar + 1
but then I got this:
BlogsId
PostsId
Order
1
1
1
1
2
2
1
2
3
3
2
3
3
4
2
4
3
5
3
5
3
6
3
6
So, I think I should do something in WHERE part (with Distinct maybe) but I don't know exactly what. I could write something in C# to do what I want but I don't know how to write something like this in SQL.
Physically maintaining an order or sequence of rows is rarely a good idea and can lead to data inconsistencies and other unforseen issues.
You would be better off creating a view that provides the additional Order column which you can do using row_number()
Create view BlogPosts as
select *,
Row_Number() over(partition by PostsId order by BlogsId) as [Order]
from blogpost;
If you really want to update an actual column in the table you could use a CTE
with b as (
select *,
Row_Number() over(partition by PostsId order by BlogsId) as seq
from blogpost
)
update b
set [Order] = seq;
You can update from a calculated row_number.
update t
set [Order] = rn
from (
select BlogsId, PostsId, [Order]
, rn = row_number() over (partition by PostsId order by BlogsId asc)
from BlogPost
) t
where ([Order] is null or [Order]!=rn);
select *
from BlogPost
order by BlogsId, PostsId
BlogsId
PostsId
Order
1
1
1
2
1
2
3
2
1
4
2
2
5
3
1
6
3
2
Demo on db<>fiddle here

using sql join on three tables

I have 3 tables which maintains stock entries for each products/items. These three tables like below :
Table : ItemStock (to maintain remaining stock of each item)
Id ItemId OpgQty BranchID CurrentStock
1 7 0 1 8
2 7 0 2 3
3 6 0 1 2
4 6 0 2 0
Table : ItemPurchase (StockIn)
Id ItemId Qty BranchID
1 7 5 1
2 7 4 2
3 7 6 1
4 7 2 2
5 6 4 1
6 6 2 2
7 6 2 1
Table : ItemSale (StockOut)
Id ItemId Qty BranchID
1 7 2 1
2 7 3 2
3 7 1 1
4 6 4 1
5 6 2 2
Desired Output (based on sql queries)
I want to have result like below : (part of report)
Id ItemId OpgQty BranchId StockIn StockOut CurrentStock
1 7 0 1 11 3 8
2 7 0 2 6 3 3
3 6 0 1 6 4 2
4 6 0 2 2 2 0
I was trying to get the desired result but was not able to do so. Please help!!!
try this;
select
m.Id,
m.ItemId,
m.OpgQty,
m.BranchID,
si.StockIn,
m.CurrentStock-si.StockIn StockOut,
m.CurrentStock
from
ItemStock m
inner join
(
select
ItemId,BranchId,sum(Qty) as StockIn
from
ItemPurchase
group by ItemId,BranchId
) si on si.ItemId=m.ItemId and si.BranchId=m.BranchId
A very simple query that gives the desired result is :
select *,
(select sum(Qty)
from ItemPurchase
where ItemPurchase.ItemId = ItemStock.ItemId and
ItemPurchase.BranchId = ItemStock.BranchId) as StockIn,
(select sum(Qty)
from ItemSale
where ItemSale.ItemId = ItemStock.ItemId and
ItemSale.BranchId = ItemStock.BranchId) as StockOut
from ItemStock
Two subqueries with group by and aggregation will get what you want.
select
s.*,
coalesce([ip].StockIn, 0) as StockIn, -- In case of no records in ItemPurchase or ItemSale, coalesce is neccessary.
coalesce([is].StockOut, 0) as StockOut
from ItemStock s
left join (
select sum(Qty) as StockIn, ItemId, BranchId
from ItemPurchase
group by ItemId, BranchId
) [ip] on s.ItemId = [ip].ItemId and s.BranchId = [ip].BranchId
left join (
select sum(Qty) as StockOut, ItemId, BranchId
from ItemSale
group by ItemId, BranchId
) [is] on s.ItemId = [is].ItemId and s.BranchId = [is].BranchId
See demo in sqlfiddle.
Please
Try This ... I hope you consider this too.

Selecting Last change value per group

I am trying to select the last change value per group.
I have a table
MMID column is incremental
MMID GID MID Value Bundle DateEntered
1 1 1 1 2 17/8/15 05:05:04
2 1 2 2 3 16/8/15 05:05:06
3 1 3 3 2 15/8/15 05:05:07
4 1 1 0 2 18/8/15 05:05:08
5 2 2 1 1 18/8/15 05:05:05
6 2 2 2 2 18/8/15 06:06:06
7 2 4 3 1 17/8/15 06:06:06
8 2 4 3 2 18/8/15 06:06:07
Here, I want the last change 'Value' in the last 24 hour(Having Date 18th August).
From the below query, I can get that. But even if the bundle value is changed, then I get that row.
But I want only rows when 'Value' is changed, or 'Value and Bundle' are changed. But not only when Bundle is changed
Desired output
MMID GID MID Value Bundle DateEntered
4 1 1 0 2 18/8/15 05:05:08
6 2 2 2 2 18/8/15 06:06:06
The query I tried is :
select yt1.*
from Table1 yt1
left outer join Table1 yt2
on (yt1.GID = yt2.GID and yt1.MID = yt2.MID
and yt1.MMID < yt2.MMID)
where yt2.MMID is null and yt2.GID is null and yt2.MID is null and yt1.DateEntered > '2015-08-18 00:00:00' ;
The output i get from here is:
MMID GID MID Value Bundle DateEntered
4 1 1 0 2 18/8/15 05:05:08
6 2 2 2 2 18/8/15 06:06:06
8 2 4 3 2 18/8/15 06:06:07
I should not be getting the last row here.
Can anyone tell me what should I change here.
Not really following the logic of your attempt, but here is how I would get the desired results:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY GID, MID ORDER BY MMID) AS rn
FROM Table
)
, cte2 AS (
SELECT t1.* FROM cte t1
INNER JOIN cte t2
ON t1.GID=t2.GID
AND t1.MID=t2.MID
AND t1.value<>t2.value
AND t1.rn=t2.rn+1
)
SELECT *
FROM cte2
WHERE MMID=(
SELECT TOP 1 MMID
FROM cte2 c2
WHERE cte2.GID=c2.GID
AND cte2.MID=c2.MID
ORDER BY MMID DESC
)
NB: If you don't want to include the rn column in the final results, use a column list instead of SELECT *.

Updating column based on another column's value

How do i update table structured like this:
id[pkey] | parent_id | position
1 1
2 1
3 1
4 1
5 1
6 2
7 2
8 2
9 2
10 3
11 3
12 3
...and so on
to achieve this result:
id[pkey] | parent_id | position
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
6 2 1
7 2 2
8 2 3
9 2 4
10 3 1
11 3 2
12 3 3
...and so on
I was thinking about somehow mixing
SELECT DISTINCT parent_id FROM cats AS t;
with
CREATE SEQUENCE dpos;
UPDATE cats t1 SET position = nextval('dpos') WHERE t.parent_id = t1.parent_id;
DROP SEQUENCE dpos;
although im not really experienced with postgres, and not sure how to use some kind of FOREACH. I appreciate any help
You can get the incremental number using row_number(). The question is how to assign it to a particular row. Here is one method using a join:
update cats
set position = c2.newpos
from (select c2.*, c2.ctid as c_ctid,
row_number() over (partition by c2.parent_id order by NULL) as seqnum
from cats c2
) c2
where cats.parent_id = c2.parent_id and cats.ctid = c2.c_ctid;
Use row_number function
select parent_id,
row_number() over (partition by parent_id order by parent_id) as position_id from table
Try this:
UPDATE table_name set table_name.dataID = v_table_name.rn
FROM
(
SELECT row_number() over (partition by your_primaryKey order by your_primaryKey) AS rn, id
FROM table_name
) AS v_table_name
WHERE v_table_name.your_primaryKey = v_table_name.your_primaryKey;