Inventory balance from two tables - sql

I have two tables in SQL Server, the first one for inventory and second for inventory movement. I need a query to show the remaining raw material for each serial number and if there's a movement or not.
Table 1
+----------+------------+-----+
| CodeRaw | Serial_Raw | Qty |
+----------+------------+-----+
| 1 | 1 | 100 |
| 1 | 2 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
| 1 | 4 | 100 |
+----------+------------+-----+
And Table 2
+------------+----------+------------+----------+--+
| CodeBatch | CodeRaw | Serial_Raw | Qty_Added| |
+------------+----------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------+----------+------------+----------+--+
I've already tried some code but I just got results where there is an inventory movenent, not showing all QT for a specific raw (Code_Raw).
Probably I'm missing something....
This is the query I have
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100),(1,4,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
SELECT t2.Serial_Raw, t1.Qty - t2.QtyAdded AS Total_Remaining FROM #tbl1 t1
INNER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.CodeRaw = 1
I expected
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20
4 100
But the result is
Serial_Raw Total_Remaining
---------- ---------------
1 10
2 0
3 20
Thanks in advance

I think your issue is your INNER JOIN. It is only returning results for your inventory that's in both tables. Meaning, if an item has no inventory movement, the total count is not returned.
Step 1: Switch your INNER JOIN to a LEFT OUTER JOIN. This will return all results in the inventory table (tbl1) even if it has no movement (tbl2). Also, select Serial_Raw from tbl1 instead of tbl2 in case of NULL value returned from JOIN.
Step 2: Step 1 will return NULL for the tbl2.QtyAdded in your JOIN. To account for this, you can do a NULL check in your calculation by using ISNULL(tbl2.QtyAdded, 0). Then, if there is not QtyAdded, the tbl1.Qty will subtract 0 (stay the same).
Resulting Code:
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100),(1,4,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
SELECT t1.Serial_Raw, t1.Qty - ISNULL(t2.QtyAdded, 0) AS Total_Remaining FROM #tbl1 t1
LEFT OUTER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2 ON t2.CodeRaw = t1.CodeRaw
AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.CodeRaw = 1
Results:
Serial_Raw Total_Remaining
-----------------------------
1 10
2 0
3 20
4 100

Use LEFT OUTER JOIN instead INNER JOIN, also ISNULL and serial_Raw from left table.
declare #tbl1 table (CodeRaw INT, Serial_Raw INT, Qty INT)
declare #tbl2 table (CodeBatch INT, CodeRaw INT, Serial_Raw INT, QtyAdded INT)
insert into #tbl1 values (1,1,100), (1,2,150), (2,1,80), (1,3,100),(1,4,100)
insert into #tbl2 values (1,1,1,80), (2,1,1,10), (3,1,2,150), (4,1,3,80)
SELECT t1.Serial_Raw, t1.Qty - ISNULL(t2.QtyAdded,0) AS Total_Remaining FROM #tbl1 t1
LEFT OUTER JOIN (SELECT CodeRaw, Serial_Raw , SUM(QtyAdded) QtyAdded FROM #tbl2
GROUP BY CodeRaw, Serial_Raw) AS t2
ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
WHERE t1.CodeRaw = 1
Result:
Serial_Raw Total_Remaining
----------- ---------------
1 10
2 0
3 20
4 100

You need to do 2 changes
Left join to the second table.
Check the second table's column is null, then set to 0 for minus
SELECT t1.Serial_Raw, t1.Qty - isnull(t2.QtyAdded, 0) AS Total_Remaining
FROM #tbl1 t1
Left Join
(SELECT CodeRaw, Serial_Raw, SUM(QtyAdded) QtyAdded FROM #tbl2 GROUP BY CodeRaw, Serial_Raw)
AS t2 ON t2.CodeRaw = t1.CodeRaw AND t1.Serial_Raw = t2.Serial_Raw
Where t1.CodeRaw = 1

Related

How to add items from another table based on a string aggregated column

I have 2 tables like this
[Table 1]:
|cust_id| tran |item |
| ------| -----|-------
| id1 | 123 |a,b,c |
| id2 | 234 |b,b |
| id3 | 345 |c,d,a,b|
[Table 2]:
| item. | value |
| ----- | ----- |
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
I want to create a target value by doing a lookup from table 2 in table 1 using big query.
|cust_id| tran.|item |target|
| ------| -----|------|------|
| id1 | 123 |a,b,c | 6
| id2 | 234 |b,b | 4
| id3 | 345 |c,d,a,b| 10
What can I try next?
Consider below simple approach
select *,
( select sum(value)
from unnest(split(item)) item
join table2
using (item)
) target
from table1
if applied to sample data in your question - output is
Try the following:
select t1.cust_id
, t1.tran
, t1.item
, sum(t2.value) as target
from table_1 t1
, UNNEST(split(t1.item ,',')) as item_unnested
LEFT JOIN table_2 t2
on item_unnested=t2.item
group by t1.cust_id
, t1.tran
, t1.item
With your data it gives the following:
Create a center table that splits the item column values on rows and join that table with table2.
Try following
--Cursor is used to split the item data row by row
--#temp is a temporary table
create table #temp (id varchar(10), trans varchar(10), item varchar(10), item1 varchar(10));
DECLARE #item varchar(10);
DECLARE #id varchar(10);
DECLARE #trans varchar(10);
DECLARE item_cusor CURSOR FOR
SELECT *
FROM table1;
OPEN item_cusor
FETCH NEXT FROM item_cusor
INTO #id,#trans,#item
WHILE ##FETCH_STATUS = 0
BEGIN
insert into #temp
SELECT #id,#trans,#item,*
FROM STRING_SPLIT (#item, ',')
FETCH NEXT FROM item_cusor
INTO #id,#trans,#item
END
CLOSE item_cusor;
DEALLOCATE item_cusor;
--select * from temp
select t.id as cust_id, t.trans,t.item , sum(cast(t2.value as int)) as target
from #temp t
JOIN table2 t2
on t.item1=t2.item
group by t.id, t.trans,t.item;
Cursors: https://www.c-sharpcorner.com/article/cursors-in-sql-server/
Temporary tables: https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/
String split function: https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

Split Row and Paste to Different Tables Based on Column

I have a table like this. Table is populated each time an order is complete. One order can have one or many compartments.
+---------+-------+-------------+------+
| OrderID | Plant | Compartment | Qty |
+---------+-------+-------------+------+
| 91 | 12 | 1 | 2000 |
| 91 | 12 | 2 | 2000 |
| 91 | 12 | 3 | 2000 |
| 90 | 12 | 1 | 3000 |
| 89 | 12 | 1 | 5000 |
+---------+-------+-------------+------+
Please help write an SQL script that takes the above and splits it into two new tables like so:
Table 1
+---------+-------+
| OrderID | Plant |
+---------+-------+
| 91 | 12 |
| 90 | 12 |
| 89 | 12 |
+---------+-------+
Table 2
+---------+-------------+------+
| OrderID | Compartment | Qty |
+---------+-------------+------+
| 91 | 1 | 2000 |
| 91 | 2 | 2000 |
| 91 | 3 | 2000 |
| 90 | 1 | 3000 |
| 89 | 1 | 5000 |
+---------+-------------+------+
I've tried using the DISTINCT command as suggested;
SELECT * FROM table
WHERE [OrderID] = (SELECT DISTINCT OrderID from table where (COMPARTMENT = '1'))
Which returns the error;
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
If the script can keep track of already processed rows so as to avoid duplication each time it runs, that would be the icing on the cake.
This is how I'd do:
-- just for debugging, your table is my #table variable table
set nocount on
declare #table table (OrderId int, Plant int, Compartment int, Qty int)
insert into #table values (89, 12, 1, 5000)
insert into #table values (90, 12, 1, 3000)
insert into #table values (91, 12, 3, 2000)
insert into #table values (91, 12, 2, 2000)
insert into #table values (91, 12, 1, 2000)
insert into #table values (91, 12, 2, 3000)
set nocount off
--select * from #table
-- now here it comes selections:
if (exists (select *
from INFORMATION_SCHEMA.TABLES
where TABLE_SCHEMA = 'dbo' -- or your schema
and TABLE_NAME = 'THeader' -- or your header's table))
insert into THeader
select distinct t1.OrderId, t1.Plant
from #table t1
left join THeader t2 on t1.OrderId = t2.OrderId and t1.Plant = t2.Plant
where t2.OrderId is null
else
select distinct t1.OrderId, t1.Plant
into THeader
from #table t1
left join THeader t2 on t1.OrderId = t2.OrderId and t1.Plant = t2.Plant
where t2.OrderId is null
if (exists (select *
from INFORMATION_SCHEMA.TABLES
where TABLE_SCHEMA = 'dbo' -- or your schema
and TABLE_NAME = 'TChilds' -- or your childs's table))
insert into TChilds
select distinct t1.OrderId, t1.Compartment, t1.Qty
from #table t1
left join TChilds t2 on t1.OrderId = t2.OrderId and t1.Compartment = t2.Compartment and t1.Qty = t2.Qty
where t2.OrderId is null
else
select distinct t1.OrderId, t1.Compartment, t1.Qty
into TChilds
from #table t1
left join TChilds t2 on t1.OrderId = t2.OrderId and t1.Compartment = t2.Compartment and t1.Qty = t2.Qty
where t2.OrderId is null
-- just for debugging
select * from THeader
select * from TChilds
Edit: Even so, I see your master table as a child table, from where you must create Header's table. It's enough. I mean your Table can be my TChilds table, and key can be OrderId + Plant. (I don't know what means plant in this table)

SQL Loop and Join

I have a table:
Vers | Rev
3 | A
7 | B
13 | C
And a second table:
Info | Version
aab | 1
adr | 2
bhj | 3
bgt | 4
nnh | 4
ggt | 7
I need to have a table:
Info | Version | Rev
aab | 1 | A
adr | 2 | A
bhj | 3 | A
bgt | 4 | B
nnh | 4 | B
ggt | 7 | B
How do I achieve the final table?
Rev A is for Versions 1-3, Rev B is versions 4-7, Rev C is versions 5-13.
If I were trying to do this with VB Excel, I would add a 1 in a new column. Then get the first Vers value (3) - second Vers value (7) then output 4....
Then I would use some logic If <= new column and >= Vers write Rev.
I don't know how to do this in SQL and I need to!
Try this you can do it by joining tables
select
t2.Info Info
,t2.Version Version
,t1.Rev Rev
from table1 t1,table2 t2
where t2.Version=t1.Vers;
Use outer apply:
select t2.*, t1.rev
from table2 t2 outer apply
(select top (1) t1.*
from table1 t1
where t2.version <= t1.vers
order by t1.vers asc
) t1;
This gets the "next" version in table1 relative to each version in table2.
You can also do this with a subquery:
SELECT *
, (SELECT TOP 1 b.rev
FROM Table1 b
WHERE a.version <= b.vers
ORDER BY b.vers)
FROM Table2 a
Or a third version:
declare #t1 table(V int, R char(1))
insert #t1 values (3,'A'),(7,'B'),(13,'C')
declare #t2 table(I char(3), V int)
insert #t2 values ('aab',1),('adr',2),('bhj',3),('bgt',4),('nnh',4),('ggt',7)
select t2.*, t1.R
from #t2 t2
join #t1 t1 on t1.V>=t2.V and not exists(select * from #t1 t3 where t3.V>=t2.v and t3.V<t1.V)

How to join two tables on one similar column and any remainder on another column

So I have:
View_1
ID | col1 | col2 | col3
---+------+------+-----
1 | a200 | null | 5
2 | null | 300 | 6
View_2:
ID | colA | colB | colC
---+------+------+-----
1 | a200 | 400 | 40
2 | a500 | 300 | 60
What I want to do is join view 1 on to view 2 on View_1.col1=View_2.colA and do any leftover View_1 rows onto View 2 using a secondary column View_1.col2=View_2.colB...so I get something like this:
| a200 | 400 | 5 | 40
| a500 | 300 | 6 | 60
I thought doing two seperate inner joins and then union would do what I want...but I think I'm getting extra data.
What's the best way to do an initial join by one column, and then any rows that didnt match see if theres an inner join between them and View 2 but on a different column?
You can do like
CREATE TABLE View_1
(
ID INT,
Col1 VARCHAR(20),
Col2 INT,
Col3 INT
);
CREATE TABLE View_2
(
ID INT,
ColA VARCHAR(20),
ColB INT,
ColC INT
);
INSERT INTO View_1 VALUES
(1, 'a200', null, 5),
(2, null, 300 , 6);
INSERT INTO View_2 VALUES
(1, 'a200', 400, 40),
(2, 'a500', 300, 60);
SELECT COALESCE(V1.Col1, V2.ColA) Column1,
COALESCE(V1.Col2, V2.ColB) Column2,
V1.Col3 Column3,
V2.ColC Column4
FROM View_1 V1 INNER JOIN View_2 V2
ON V1.Col1 = V2.ColA OR V1.Col2 = V2.ColB;
Results:
+----+---------+---------+---------+---------+
| | Column1 | Column2 | Column3 | Column4 |
+----+---------+---------+---------+---------+
| 1 | a200 | 400 | 5 | 40 |
| 2 | a500 | 300 | 6 | 60 |
+----+---------+---------+---------+---------+
Demo
You can just use an OR condition with your INNER JOIN and then use CASE Statements to return non-nulls, this solution returns your example of what you want returned:
DECLARE #temp1 TABLE ([Id] INT IDENTITY(1, 1), [col1] varchar(10), [col2] INT, [col3] INT)
DECLARE #temp2 TABLE([Id] INT IDENTITY(1, 1), [col1] varchar(10), [colB] INT, [colC] INT)
INSERT INTO #temp1
([col1], [col2], [col3])
VALUES('a200', NULL, 5), (NULL, 300, 6)
INSERT INTO #temp2
([col1], [colB], [colC])
VALUES('a200', 400, 40), ('a500', 300, 60)
SELECT CASE WHEN t1.[col1] IS NULL THEN t2.[col1] ELSE t1.[col1] END AS [col1], CASE WHEN t1.[col2] IS NULL THEN t2.[colB] ELSE t1.[col2] END AS [col2], t1.[col3], t2.[colC]
FROM #temp1 AS t1
INNER JOIN #temp2 AS t2
ON t2.[col1] = t1.[col1] OR t2.[colB] = t1.[col2]

SQL Column title from another table

I have two tables
Index | Name | GroupIndex \\table1
1 | A | 1
2 | A | 1
3 | A | 2
Index | GroupName \\table2
1 | G1
2 | G2
I would like my result to be
Index | Name | G1 | G2
1 | A | 2 | 1
where I need to display the number of entries of Name grouped by GroupIndex, but in a column format.
I couldn't find a way to add columns dynamically, from another table. Can you help me out?
create table #table1 ([Index] int,Name char(1), GroupIndex int)
create table #table2 ([Index] int,GroupName char(2))
insert into #table1 values
(1,'A',1),
(2,'A',1),
(3,'A',2)
insert into #table2 values
(1,'G1'),
(2,'G2')
--select * from #table1
--select * from #table2
select * from
(select t1.[GroupIndex] as [Index], t1.Name, t2.[Index] as gi, t2.GroupName
from #table1 t1
inner join #table2 t2 on t2.[Index] = t1.[Index]) as s
PIVOT
(max(gi) FOR GroupName in (G1,G2)) as pvt
--RESULTS--
Index Name G1 G2
1 A 1 2
create a pivot that group your user by name then count their groups using sum function for each group.
SELECT
Sum(case when tbl2.[index]=1 THEN 1 ELSE 0 end ) AS G1,
SUM(case when tbl2.[index]=2 THEN 1 ELSE 0 end ) AS G2
FROM tbl1
join tbl2 on tbl2.[index] = tbl1.GroupIndex group by tbl1.Name