Finding missing sequence number in SQL 2008 table - sql-server-2008

I have a table that return the below by this query:
SELECT *
FROM [PROC_MN]
where PO_NO='GV17885' AND DOC_NO='622843'
ID PO_NO DOC_NO PROCESS_SEQ PROCESS_NAME STATUS TIME
756 GV17885 622843 2 R.M.Requisition Start 23-04-18 15:29
788 GV17885 622843 2 R.M.Requisition Finish 23-04-18 15:50
289 GV17885 622843 1 CTP Start 23-04-18 8:57
426 GV17885 622843 1 CTP Finish 23-04-18 10:09
901 GV17885 622843 3 Material Cut Start 23-04-18 17:23
903 GV17885 622843 3 Material Cut Finish 23-04-18 17:26
1669 GV17885 622843 4 Print Start 24-04-18 13:59
1712 GV17885 622843 4 Print Finish 24-04-18 14:44
3421 GV17885 622843 5 Q.C Start 27-04-18 8:04
3492 GV17885 622843 5 Q.C Finish 27-04-18 8:42
3630 GV17885 622843 7 RFID Start 27-04-18 9:36
3632 GV17885 622843 7 RFID Finish 27-04-18 9:38
4264 GV17885 622843 8 Q.C Start 27-04-18 14:58
4288 GV17885 622843 8 Q.C Finish 27-04-18 15:16
4729 GV17885 622843 9 Encode Start 28-04-18 8:48
4734 GV17885 622843 9 Encode Finish 28-04-18 8:49
4698 GV17885 622843 9 Encode Start 28-04-18 8:24
4722 GV17885 622843 9 Encode Finish 28-04-18 8:47
5016 GV17885 622843 10 Q.C Start 28-04-18 13:38
5073 GV17885 622843 10 Q.C Finish 28-04-18 14:11
I want to find the missing PROCESS_SEQ row, with the above result we found that the missing PROCESS_SEQ is number 6 . I use the following query but it return nothing/. Please help!
SET NOCOUNT ON;
DECLARE #Min BIGINT
DECLARE #Max BIGINT
DECLARE #i BIGINT
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
CREATE TABLE #TempTable
(
TempOrderNumber BIGINT
)
SELECT #Min = ( SELECT MIN([PROCESS_SEQ])
FROM [PROC_MN] WITH ( NOLOCK )
WHERE PO_NO='GV17885' AND [DOC_NO]='622843')
SELECT #Max = ( SELECT MAX([PROCESS_SEQ])
FROM [PROC_MN] WITH ( NOLOCK )
WHERE PO_NO='GV17885' AND [DOC_NO]='622843')
SELECT #i = #Min
WHILE #i <= #Max
BEGIN
INSERT INTO #TempTable
SELECT #i
SELECT #i = #i + 1
END
SELECT TempOrderNumber
FROM #TempTable
LEFT JOIN [PROC_MN] o WITH ( NOLOCK ) ON tempordernumber = o.[PROCESS_SEQ]
WHERE o.[PROCESS_SEQ] IS NULL

You may try the following query:
WITH
[MISSING_RANGES] AS
(
SELECT
[MISSING_RANGE_NO] = ROW_NUMBER() OVER (ORDER BY P.[PO_NO], P.[DOC_NO], P.[PROCESS_SEQ]),
P.[PO_NO],
P.[DOC_NO],
[FIRST_MISSING] = P.[PROCESS_SEQ] + 1,
[LAST_MISSING] = N.[PROCESS_SEQ] - 1
FROM
[PROC_MN] AS P
OUTER APPLY (SELECT [PROCESS_SEQ] = MIN([PROCESS_SEQ])
FROM [PROC_MN]
WHERE
[PO_NO] = P.[PO_NO] AND
[DOC_NO] = P.[DOC_NO] AND
[PROCESS_SEQ] > P.[PROCESS_SEQ]) AS N
WHERE
N.[PROCESS_SEQ] > P.[PROCESS_SEQ] + 1
),
[MISSING_SEQUENCES] AS
(
SELECT
[MISSING_RANGE_NO],
[MISSING_PROCESS_SEQ] = [FIRST_MISSING]
FROM
[MISSING_RANGES]
UNION ALL
SELECT
R.[MISSING_RANGE_NO],
S.[MISSING_PROCESS_SEQ] + 1
FROM
[MISSING_RANGES] AS R
INNER JOIN [MISSING_SEQUENCES] AS S ON S.[MISSING_RANGE_NO] = R.[MISSING_RANGE_NO]
WHERE
S.[MISSING_PROCESS_SEQ] < R.[LAST_MISSING]
)
SELECT DISTINCT
R.[PO_NO],
R.[DOC_NO],
S.[MISSING_PROCESS_SEQ]
FROM
[MISSING_RANGES] AS R
INNER JOIN [MISSING_SEQUENCES] AS S ON S.[MISSING_RANGE_NO] = R.[MISSING_RANGE_NO]
--WHERE
-- R.[PO_NO] = ? AND
-- R.[DOC_NO] = ?
ORDER BY
R.[PO_NO],
R.[DOC_NO],
S.[MISSING_PROCESS_SEQ]
OPTION (MAXRECURSION 10000);
(I put the WHERE clause to filter for specific [PO_NO] AND [DOC_NO] values in comment for testing purposes.)
Edit (in response to the additional updated post):
This is my personal version of your SQL-script. Just like yours, it generates a temp table containing a sequence (from minimum to maximum) and selects all values where the record from the data table cannot be joined:
DECLARE #PO_NO VARCHAR(10) = 'GV17885';
DECLARE #DOC_NO VARCHAR(10) = '622843';
DECLARE #TempTable TABLE
(
[TempOrderNumber] BIGINT PRIMARY KEY
);
DECLARE #Min BIGINT;
DECLARE #Max BIGINT;
SELECT
#Min = MIN([PROCESS_SEQ]),
#Max = MAX([PROCESS_SEQ])
FROM
[PROC_MN] WITH (NOLOCK)
WHERE
[PO_NO] = #PO_NO AND
[DOC_NO] = #DOC_NO;
DECLARE #i BIGINT = #Min;
WHILE (#i <= #Max) BEGIN
INSERT INTO #TempTable ([TempOrderNumber])
SELECT #i;
SET #i += 1;
END;
SELECT
[TempOrderNumber]
FROM
#TempTable
LEFT JOIN [PROC_MN] AS O WITH (NOLOCK) ON
[TempOrderNumber] = O.[PROCESS_SEQ]
AND O.[PO_NO] = #PO_NO
AND O.[DOC_NO] = #DOC_NO
WHERE
O.[PROCESS_SEQ] IS NULL;
This should work correctly too. But I personally prefer my original query, since it is based on set operations instead of loops.

Related

How to return the number of sets for each date

I am running a query that returns:
Date Set Data
2021-07-02 1 A
2021-07-02 2 B
2021-07-02 3 C
2021-08-15 1 D
2021-10-27 1 E
2021-10-27 2 F
I need to also return the number of Sets for each date:
Date Set Data NoSets
2021-07-02 1 A 3
2021-07-02 2 B 3
2021-07-02 3 C 3
2021-08-15 1 D 1
2021-10-27 1 E 2
2021-10-27 2 F 2
SELECT csm_pat_exe_date.pedate_id, csm_patient_exercise.pat_exe_id,
csm_exercise_details.ede_id, csm_exercise.exe_id,
ses_pat_id, ses_pat_note, ses_pat_body_weight, ses_pat_blood_pressure, ses_pat_blood_glucose_level,
exe_name, ede_order, ede_type,
ede_unit, weekofyear(csm_pat_exe_date.pedate_date) AS weekNumber, csm_pat_exe_date.pedate_date, peds_id, peds_set, peds_result, pedate_note, t2.noSets
FROM csm_session_patient, csm_session, csm_exercise, csm_patient_exercise, csm_exercise_details,
csm_pat_exe_date_set, csm_pat_exe_date
INNER JOIN (
SELECT pedate_date, COUNT(*) as noSets
FROM csm_patient_exercise, csm_pat_exe_date
WHERE csm_patient_exercise.pat_id = '1'
AND csm_patient_exercise.pat_exe_id = csm_pat_exe_date.pat_exe_id
GROUP BY pedate_date
) t2 ON csm_pat_exe_date.pedate_date = t2.pedate_date
WHERE csm_session_patient.pat_id= '1'
AND csm_session_patient.ses_id = csm_session.ses_id
AND csm_session.ses_date = csm_pat_exe_date.pedate_date
AND exe_archived IS NULL
AND csm_exercise.exe_id = csm_patient_exercise.exe_id
AND csm_patient_exercise.pat_exe_id = '1'
AND csm_patient_exercise.exe_id = csm_patient_exercise.pat_exe_id
AND csm_patient_exercise.pat_exe_id = csm_pat_exe_date.pat_exe_id
AND csm_pat_exe_date.pedate_date >= '2021-06-01'
AND csm_pat_exe_date.pedate_date <= '2021-09-24'
AND csm_pat_exe_date.pedate_id = csm_pat_exe_date_set.pedate_id
AND csm_pat_exe_date_set.ede_id = csm_exercise_details.ede_id
ORDER BY csm_pat_exe_date.pedate_date, peds_set, ede_order;
You may add the count window function to your select clause eg
Mysql 8+
SELECT
`Date`,
`Set`,
`Data`,
`NoSets`,
COUNT(*) OVER (PARTITION BY Date) as NoSets
FROM
...include the rest of your query here
Older Mysql Versions
You may use variables or aggregates to achieve your count
Schema (MySQL v5.5)
CREATE TABLE my_table (
`Date` DATETIME,
`Set` INTEGER,
`Data` VARCHAR(1)
);
INSERT INTO my_table
(`Date`, `Set`, `Data`)
VALUES
('2021-07-02', '1', 'A'),
('2021-07-02', '2', 'B'),
('2021-07-02', '3', 'C'),
('2021-08-15', '1', 'D'),
('2021-10-27', '1', 'E'),
('2021-10-27', '2', 'F');
Query #1
SELECT
`Date`,
`Set`,
`Data`,
`NoSets`
FROM (
SELECT
t.*,
#maxcnt:=IF(#prevdate2=`DATE`,IF(#maxcnt>cnt,#maxcnt,cnt),cnt) as NoSets,
#prevdate2:=`Date`
FROM (
SELECT
#cnt:=IF(#prevdate1=`DATE`,#cnt+1,1) as cnt,
#prevdate1:=`Date`,
m.*
FROM
my_table m
CROSS JOIN (SELECT #cnt:=0,#prevdate1:=NULL) vars
ORDER BY `Date`
) t
CROSS JOIN (SELECT #maxcnt:=0,#prevdate2:=NULL) vars
ORDER BY `Date`,cnt DESC
) t2;
Date
Set
Data
NoSets
2021-07-02 00:00:00
3
C
3
2021-07-02 00:00:00
2
B
3
2021-07-02 00:00:00
1
A
3
2021-08-15 00:00:00
1
D
1
2021-10-27 00:00:00
2
F
2
2021-10-27 00:00:00
1
E
2
Query #2
SELECT
t1.`Date`,
t1.`Set`,
t1.`Data`,
t2.`NoSets`
FROM
my_table t1
INNER JOIN (
SELECT `Date`, COUNT(*) as NoSets
FROM my_table
GROUP BY `Date`
) t2 ON t1.`Date`=t2.`Date`;
Date
Set
Data
NoSets
2021-07-02 00:00:00
1
A
3
2021-07-02 00:00:00
2
B
3
2021-07-02 00:00:00
3
C
3
2021-08-15 00:00:00
1
D
1
2021-10-27 00:00:00
1
E
2
2021-10-27 00:00:00
2
F
2
or
SELECT
t1.`Date`,
t1.`Set`,
t1.`Data`,
(
SELECT COUNT(*) FROM my_table t2 WHERE t2.Date=t1.Date
) as `NoSets`
FROM
my_table t1
View on DB Fiddle
Let me know if this works for you.

Keeping a Running Total of Inventory Quantity in MySQL Database Query/Operation for same SKU/entries

Here is my table ws_sold I'm doing testing with
Along with my ws_inventory table
My query is as followed:
SELECT
inventory.id,
inventory.sku AS inventory_sku,
inventory.quantity AS inventory_quantity,
mastersku.sku2,
mastersku.sku1,
mastersku.sku3,
mastersku.multsku,
mastersku.qtysku,
mastersku.altsku,
mastersku.sku,
sold.quantity AS sold_quantity,
sold.sku AS sold_sku
FROM sold
LEFT OUTER JOIN mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
Which has an output of:
Everything is great, besides the inventory_quantity column results.
My query is not taking into consideration previous equations in earlier rows for same SKU entries, and is assuming each query is starting fresh from the ws_inventory Quantity of 99.
The logic in this is (I've done so with both PHP and MySQL in testing as I'm open to both):
inventory_quantity - (sold_quantity * ws_mastersku.QtySKU)
Therefore the first result for WS16 is 99 - (2 * 4) = 91.
This is correct.
But, the second instance of WS16 is 99 - (4 * 4) = 83.
And is therefore over-writing the first result.
I'm looking for a query that will keep the running total on inventory_quantity if (such as in this test case), there are more than one of the same SKU being processed.
Something such as this:
1 WS16 91 (null) (null) (null) (null) 0 4 WS16 WS16X4-2 WS16X4-2 2
2 WS3 97 (null) (null) (null) (null) 0 2 WS3 WS3X2-4 WS3X2-4 1
3 WS6 95 (null) (null) (null) (null) 0 4 WS6 WS6X4-16 WS6X4-16 1
4 WS16 75 (null) (null) (null) (null) 0 4 WS16 WS16X4-2 WS16X4-2 4
I realize this issue is arrising because inventory_quantity is taken at the start the query as its initial number, and is not updating based off processes later down in the line.
Any suggestions/help please? It has taken me a while just to get to this point in the project, being rather new to MySQL it has been a big learning experience all the way through, but this issue is causing a huge barrier for me.
Thank you!
You should reconsider your database design and the questions you need to answer. You are using the ws_inventory.quantity field to store the initial quantity when in fact it should be showing the current available quantity. This will instantly show the answer to the question: "how many widgets do I have left?"
You should be decrementing the inventory in ws_inventory as you sell each unit. You can do this with a trigger in MySQL when you add a quantity to the ws_sold table you update the ws_inventory.quantity field. You may also want to do this as a separate query in your application (PHP?) code. The quantity should show quantity on hand: that way if you have 99 and sell 2 and then sell 3 more your ws_inventory.quantity field should be 94 (94 = 99 - 2 - 3). You can also add to this field when you replenish inventory.
This is how the trigger should work:
(from Update another table after insert using a trigger?)
-- run this SQL code on your database
-- note that NEW.quantity and NEW.sku are what you inserted into ws_sold
CREATE TRIGGER update_quantity
AFTER INSERT ON ws_sold
FOR EACH ROW
UPDATE ws_inventory
SET ws_inventory.quantity = ws_inventory.quantity - NEW.quantity
WHERE ws_inventory.sku = NEW.sku;
If you need to maintain a history of inventory for reports like the one above, then you may want to consider an ws_inventory_history table that can take snapshots.
The following answer works by joining the sold items with the last sold items after grouping them on inventory.id and by resetting the variables when the inventory.id changes. Note how the join is on the inventory.id and group_row - 1 = group_row
This SQL example works
select sold.inventory_id
, sold.sold_id
, case sold.inventory_id when #inventoryId then
#starting := #starting
else
#starting := sold.inventory_quantity_before_sale
end as starting_quantity
, case sold.inventory_id when #inventoryId then
#runningSold := #runningSold + coalesce(last.quantity_sold,0)
else
#runningSold := 0
end as running_sold
, case sold.inventory_id when #inventoryId then
#runningInventoryQuantity := #starting - #runningSold
else
#runningInventoryQuantity := sold.inventory_quantity_before_sale
end as before_sale
, sold.quantity_sold
, sold.inventory_quantity_before_sale - sold.quantity_sold - #runningSold after_sale
, #inventoryId := sold.inventory_id group_inventory_id -- clocks over the group counter
from (
select inventorySold.*
, case inventory_id
when #inventoryId then
#groupRow := #groupRow + 1
else
#groupRow := 1
end as group_row
, #inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity_before_sale
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select #groupRow := 0, #inventoryId := 0 ) variables
order by inventory_id, sold_id
) sold
left join (
select inventorySold.*
, case inventory_id
when #inventoryId then
#groupRow := #groupRow + 1
else
#groupRow := 1
end as group_row
, #inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select #groupRow := 0, #inventoryId := 0 ) variables
order by inventory_id, sold_id
) `last`
on sold.inventory_id = `last`.inventory_id
and sold.group_row - 1 = `last`.group_row
join ( select #runningInventoryQuantity := 0, #runningSold := 0, #inventoryId := 0, #afterSold := 0, #starting :=0 ) variables
order by sold.inventory_id, sold.group_row
-- example results
inventory_id sold_id starting_quantity running_sold before_sale quantity_sold after_sale group_inventory_id
1 1 93 0 93 4 89 1
1 4 93 4 89 16 73 1
1 5 93 20 73 20 53 1
1 12 93 40 53 48 5 1
2 2 97 0 97 4 93 2
2 6 97 4 93 12 81 2
2 7 97 16 81 14 67 2
2 8 97 30 67 16 51 2
2 11 97 46 51 22 29 2
3 3 95 0 95 12 83 3
3 9 95 12 83 36 47 3
3 10 95 48 47 40 7 3
You could do the same thing in PHP. Have a starting quantity, a running amount sold and a running total that gets reset each time the inventory id changes and then use the quantity sold for each transaction to adjust those variables.
If you choose the PHP route example sql is
select sold.* from (
select inventorySold.*
, case inventory_id
when #inventoryId then
#groupRow := #groupRow + 1
else
#groupRow := 1
end as group_row
, #inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity_before_sale
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select #groupRow := 0, #inventoryId := 0 ) variables
order by inventory_id, sold_id
) sold
-- example results
inventory_id inventory_quantity_before_sale quantity_sold sold_id group_row group_inventory_id
1 93 4 1 1 1
1 93 16 4 2 1
1 93 20 5 3 1
1 93 48 12 4 1
2 97 4 2 1 2
2 97 12 6 2 2
2 97 14 7 3 2
2 97 16 8 4 2
2 97 22 11 5 2
3 95 12 3 1 3
3 95 36 9 2 3
3 95 40 10 3 3
You can get the label information by joining other tables to the result using inventory.id and sold.id.
I agree with https://stackoverflow.com/users/932820/chris-adams. If you're looking to keep a track of stocktakes over time then you'll need a transaction table to record the starting and ending inventory quantities and starting and ending timestamps ... and probably starting and ending sold ids.
-- supporting tables - run this in an empty database unless you want to destroy your current tables
DROP TABLE IF EXISTS `ws_inventory`;
CREATE TABLE `ws_inventory` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `ws_inventory` WRITE;
/*!40000 ALTER TABLE `ws_inventory` DISABLE KEYS */;
INSERT INTO `ws_inventory` (`id`, `sku`, `quantity`)
VALUES
(1,'WS16',93),
(2,'WS3',97),
(3,'WS6',95);
/*!40000 ALTER TABLE `ws_inventory` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table ws_mastersku
# ------------------------------------------------------------
DROP TABLE IF EXISTS `ws_mastersku`;
CREATE TABLE `ws_mastersku` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) DEFAULT NULL,
`sku1` varchar(20) DEFAULT NULL,
`sku2` varchar(20) DEFAULT NULL,
`sku3` varchar(20) DEFAULT NULL,
`multsku` tinyint(2) DEFAULT NULL,
`qtysku` int(11) DEFAULT NULL,
`altsku` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `ws_mastersku` WRITE;
/*!40000 ALTER TABLE `ws_mastersku` DISABLE KEYS */;
INSERT INTO `ws_mastersku` (`id`, `sku`, `sku1`, `sku2`, `sku3`, `multsku`, `qtysku`, `altsku`)
VALUES
(1,'WS16X4-2',NULL,NULL,NULL,NULL,4,'WS16'),
(2,'WS3X2-4',NULL,NULL,NULL,NULL,2,'WS3'),
(3,'WS6X4-16',NULL,NULL,NULL,NULL,4,'WS6');
/*!40000 ALTER TABLE `ws_mastersku` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table ws_sold
# ------------------------------------------------------------
DROP TABLE IF EXISTS `ws_sold`;
CREATE TABLE `ws_sold` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `ws_sold` WRITE;
/*!40000 ALTER TABLE `ws_sold` DISABLE KEYS */;
INSERT INTO `ws_sold` (`id`, `sku`, `quantity`)
VALUES
(1,'WS16X4-2',1),
(2,'WS3X2-4',2),
(3,'WS6X4-16',3),
(4,'WS16X4-2',4),
(5,'WS16X4-2',5),
(6,'WS3X2-4',6),
(7,'WS3X2-4',7),
(8,'WS3X2-4',8),
(9,'WS6X4-16',9),
(10,'WS6X4-16',10),
(11,'WS3X2-4',11),
(12,'WS16X4-2',12);
/*!40000 ALTER TABLE `ws_sold` ENABLE KEYS */;
UNLOCK TABLES;
Using the schema you supplied.
The query for SQL.
select sold.inventory_id
, sold.sold_id
, case sold.inventory_id when #inventoryId then
#starting := #starting
else
#starting := sold.inventory_quantity_before_sale
end as starting_quantity
, case sold.inventory_id when #inventoryId then
#runningSold := #runningSold + coalesce(last.quantity_sold,0)
else
#runningSold := 0
end as running_sold
, case sold.inventory_id when #inventoryId then
#runningInventoryQuantity := #starting - #runningSold
else
#runningInventoryQuantity := sold.inventory_quantity_before_sale
end as before_sale
, sold.quantity_sold
, sold.inventory_quantity_before_sale - sold.quantity_sold - #runningSold after_sale
, #inventoryId := sold.inventory_id group_inventory_id -- clocks over the group counter
from (
select inventorySold.*
, case inventory_id
when #inventoryId then
#groupRow := #groupRow + 1
else
#groupRow := 1
end as group_row
, #inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity_before_sale
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku_1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select #groupRow := 0, #inventoryId := 0 ) variables
order by inventory_id, sold_id
) sold
left join (
select inventorySold.*
, case inventory_id
when #inventoryId then
#groupRow := #groupRow + 1
else
#groupRow := 1
end as group_row
, #inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku_1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select #groupRow := 0, #inventoryId := 0 ) variables
order by inventory_id, sold_id
) `last`
on sold.inventory_id = `last`.inventory_id
and sold.group_row - 1 = `last`.group_row
join ( select #runningInventoryQuantity := 0, #runningSold := 0, #inventoryId := 0, #afterSold := 0, #starting :=0 ) variables
order by sold.inventory_id, sold.group_row
-- example results
inventory_id sold_id starting_quantity running_sold before_sale quantity_sold after_sale group_inventory_id
1 1 99 0 99 8 91 1
1 4 99 8 91 16 75 1
2 2 99 0 99 2 97 2
3 3 99 0 99 4 95 3
The query for PHP.
select sold.* from (
select inventorySold.*
, case inventory_id
when #inventoryId then
#groupRow := #groupRow + 1
else
#groupRow := 1
end as group_row
, #inventoryId := inventory_id as group_inventory_id
from (
SELECT
inventory.id inventory_id
, inventory.quantity AS inventory_quantity_before_sale
, mastersku.qtysku * sold.quantity quantity_sold
, sold.id as sold_id -- for order ... you'd probably use created timestamp or finalised timestamp
FROM ws_sold sold
LEFT OUTER JOIN ws_mastersku mastersku
ON sold.sku = mastersku.sku
LEFT OUTER JOIN ws_inventory inventory
ON mastersku.sku_1 = inventory.sku
OR mastersku.altsku = inventory.sku
) inventorySold
join ( select #groupRow := 0, #inventoryId := 0 ) variables
order by inventory_id, sold_id
) sold
-- example results
inventory_id inventory_quantity_before_sale quantity_sold sold_id group_row group_inventory_id
1 99 8 1 1 1
1 99 16 4 2 1
2 99 2 2 1 2
3 99 4 3 1 3

SQL - Group Table by entries X amount of time from each other

I need to group together the entries in which the timestamp difference between one and the other is X amount of seconds or less than then average the value for each of them for each of the devices. In the following example I have a Table with this data and I need to group by device with entries between 60 seconds from each other.
Device Timestamp Value
0 30:8c:fb:a4:b9:8b 10/26/2015 22:50:15 34
1 30:8c:fb:a4:b9:8b 10/26/2015 22:50:46 34
2 c0:ee:fb:35:ec:cd 10/26/2015 22:50:50 33
3 c0:ee:fb:35:ec:cd 10/26/2015 22:50:51 32
4 30:8c:fb:a4:b9:8b 10/26/2015 22:51:15 34
5 30:8c:fb:a4:b9:8b 10/26/2015 22:51:47 32
6 c0:ee:fb:35:ec:cd 10/26/2015 22:52:38 38
7 30:8c:fb:a4:b9:8b 10/26/2015 22:54:46 34
This should be the resulting Table
Device First_seen Last_seen Average_value
0 30:8c:fb:a4:b9:8b 10/26/2015 22:50:15 10/26/2015 22:51:47 33,5
1 c0:ee:fb:35:ec:cd 10/26/2015 22:50:50 10/26/2015 22:50:51 32,5
2 c0:ee:fb:35:ec:cd 10/26/2015 22:52:38 10/26/2015 22:52:38 38
3 30:8c:fb:a4:b9:8b 10/26/2015 22:54:46 10/26/2015 22:54:46 34
Thank you very much for your help.
There is an old trick for this!
Mostly based on power of Window functions
Perfectly works for BigQuery!
So, first you "mark" all entries which exceed 60 seconds after previous entry!
Those which exceed getting value 1 and rest getting value 0!
Secondly you define groups by summing all previous marks (of course steps above are done while partitioning by device)
And finally, you just do simple grouping by above defined groups
Three simple steps implemented in one query with few simple sub-selects!
Hope this helps
SELECT device, MIN(ts) AS first_seen, MAX(ts) AS last_seen, AVG(value) AS average_value
FROM (
SELECT device, ts, value, SUM(grp_start) OVER (PARTITION BY device ORDER BY ts) AS grp
FROM (
SELECT device, ts, value,
IF(TIMESTAMP_TO_SEC(TIMESTAMP(ts))-TIMESTAMP_TO_SEC(TIMESTAMP(ts0))>60,1,0) AS grp_start
FROM (
SELECT device, ts, value, LAG(ts, 1) OVER(PARTITION BY device ORDER BY ts) AS ts0
FROM yourTable
)
)
)
GROUP BY device, grp
Here's one way...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(device CHAR(1) NOT NULL
,timestamp DATETIME NOT NULL
,value INT NOT NULL
,PRIMARY KEY(device,timestamp)
);
INSERT INTO my_table VALUES
('a','2015/10/26 22:50:15',34),
('a','2015/10/26 22:50:46',34),
('b','2015/10/26 22:50:50',33),
('b','2015/10/26 22:50:51',32),
('a','2015/10/26 22:51:15',34),
('a','2015/10/26 22:51:47',32),
('b','2015/10/26 22:52:38',38),
('a','2015/10/26 22:54:46',34);
SELECT m.*
, AVG(n.value) avg
FROM
( SELECT a.device
, a.timestamp start
, MIN(c.timestamp) end
FROM
( SELECT x.*
, CASE WHEN x.device = #prev THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=device
FROM my_table x
, (SELECT #i:=1,#prev:=null) vars
ORDER
BY device
, timestamp
) a
LEFT
JOIN
( SELECT x.*
, CASE WHEN x.device = #prev THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=device
FROM my_table x
, (SELECT #i:=1,#prev:=null) vars
ORDER
BY device
, timestamp
) b
ON b.device = a.device
AND b.timestamp > a.timestamp - INTERVAL 60 SECOND
AND b.i = a.i - 1
LEFT
JOIN
( SELECT x.*
, CASE WHEN x.device = #prev THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=device
FROM my_table x
, (SELECT #i:=1,#prev:=null) vars
ORDER
BY device
, timestamp
) c
ON c.device = a.device
AND c.i >= a.i
LEFT
JOIN
( SELECT x.*
, CASE WHEN x.device = #prev THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=device
FROM my_table x
, (SELECT #i:=1,#prev:=null) vars
ORDER
BY device
, timestamp
) d
ON d.device = c.device
AND d.i = c.i + 1
AND d.timestamp < c.timestamp + INTERVAL 60 SECOND
WHERE b.i IS NULL
AND c.i IS NOT NULL
AND d.i IS NULL
GROUP
BY a.device
, a.i
) m
JOIN my_table n
ON n.device = m.device
AND n.timestamp BETWEEN start AND end
GROUP
BY m.device
, m.start;
+--------+---------------------+---------------------+---------+
| device | start | end | avg |
+--------+---------------------+---------------------+---------+
| a | 2015-10-26 22:50:15 | 2015-10-26 22:51:47 | 33.5000 |
| a | 2015-10-26 22:54:46 | 2015-10-26 22:54:46 | 34.0000 |
| b | 2015-10-26 22:50:50 | 2015-10-26 22:50:51 | 32.5000 |
| b | 2015-10-26 22:52:38 | 2015-10-26 22:52:38 | 38.0000 |
+--------+---------------------+---------------------+---------+

SUM from two tables, sort by country and publisher

I have two different sales tables and i want to SUM the quantity and FEEs for storeX and storeY from book_sales(isa), with quantity from all sales in the financial_report(xo). Im having trouble with both, and i dont know which JOIN i should use. The quantity calculation does not give any results and the fee calculation gives a number that is way to high. What is wrong here?
Please check my procedure if you have time:
ALTER PROCEDURE [dbo].[Report] #startDate VARCHAR(10),
#endDate VARCHAR(10)
AS
SELECT BB.name AS 'Publisher',
Sum(CASE
WHEN isa.report_source = 'storeX'
or isa.report_source = 'storeY' THEN
isa.quantity * xo.quantity
END) AS 'Total quantity',
Sum(CASE
WHEN isa.sales_price > 69
AND isa.report_source = 'storeX' THEN
6 * 1.25 * isa.quantity
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeX' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeX' THEN
1 * 1.25 * isa.quantity
WHEN isa.sales_price > 69
AND isa.report_source = 'storeY' THEN
6 * 1.25 * isa.quantity
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeY' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeY' THEN
1 * 1.25 * isa.quantity
WHEN xo.sales_price > 69
AND bb.country = 'NOR' THEN 6 * 1.25 * xo.quantity
WHEN xo.sales_price <= 69
AND bb.country = 'NOR' THEN 3 * 1.25 * xo.quantity
WHEN xo.sales_price > 69
AND bb.country <> 'NOR' THEN 6 * xo.quantity
WHEN xo.sales_price <= 69
AND bb.country <> 'NOR' THEN 3 * xo.quantity
END) AS 'Fee inc VAT(tot)'
FROM book_sales AS isa
INNER JOIN store AS BV
ON bv.store_id = isa.store_id
INNER JOIN financial_report AS xo
ON xo.identifiers = isa.identifiers
LEFT OUTER JOIN publisher AS BB
ON bb.publisher_id = bk.publisher_id
WHERE isa.sales_date >= CONVERT(DATETIME, #startDate, 20)
AND isa.sales_date < Dateadd(day, 1, ( CONVERT(DATETIME, #endDate, 20
) ))
GROUP BY bb.name,
bb.country
There are a few problems to sort out :
1- The total might be affected by NULL values, I suggest:
Sum(CASE
WHEN isa.report_source = 'storeX'
or isa.report_source = 'storeY' THEN
isnull(isa.quantity,0) * isnull(xo.quantity,0)
ELSE 0 END) AS 'Total quantity',
2- The case evaluation should be in order. For example:
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeX' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeX' THEN
1 * 1.25 * isa.quantity
<= 19 will never happen because it is also <= 69 and therefore the <=69 will evaluate true.
3- Cardinality between tables might be 1 to many so the values are summed multiple times when they shouldn't. You may need to select distinct or aggregate or flatten out some of the tables in the join to avoid this issue.

compare one column values with same column values of data in sql server?

ALTER proc [dbo].[K_RT_GetRatebasedonmeat]
#partyname int,
#meattype int
AS
BEGIN
SELECT
CASE WHEN PE.meattype <> (SELECT meattype
FROM K_RT_PartyNameYearly
WHERE partyname = #partyname) THEN 0
ELSE ISNULL(PE.rateperkg, 0)
END AS rateperkg
FROM
K_RT_PartyNameYearly PE
INNER JOIN
K_RT_PartyName PN ON PE.partysno = PN.sno
WHERE
PE.partysno = #partyname
AND PE.meattype = #meattype
END
out put:
PE.partysno PE.meattype PE.rateperkg
36 3 150
36 2 125
25 2 100
if i pass partysno=36 and meattype=1 at that it will show 0 because there is no data for meattype 1 for that partysno
I wrote like this for my requirement but its not working properly. When I pass partyname and meattype as parameters, if K_RT_PartyNameYearly this table does not contain meattype for that partyname at that time I want to show 0 value. How can I write this? Please help me.
TRY THIS...
ALTER proc [dbo].[K_RT_GetRatebasedonmeat]
#partysno int,
#meattype int
AS
BEGIN
if exists
( SELECT 1 FROM K_RT_PartyNameYearly WHERE partysno = #partysno AND meattype = #meattype)
BEGIN
SELECT
PE.partysno ,
PE.meattype,
PE.rateperkg
FROM
K_RT_PartyNameYearly PE
WHERE
PE.partysno = #partysno AND PE.meattype = #meattype
END
ELSE
BEGIN
SELECT distinct
partysno partysno ,
null meattype,
0 AS rateperkg
FROM
K_RT_PartyNameYearly PE
WHERE
PE.partysno = #partysno
END
END