Combine the result set from both tables based on inno - sql

I have a table1 with following structure and data:
header type agrid inno stallno
0 D 14 001 000
0 D 14 008 000
0 N 14 008 000
Another table with following structure and data:
header type agrid inno stallno
1 NULL 14 001 000
1 NULL 14 008 000
How can I achieve the below result set: I tried row_number it works for couple of data sets it doesn't work for other
header type agrid inno
0 D 14 001
1 NULL 14 001
0 D 14 008
1 NULL 14 008
0 N 14 008
1 NULL 14 008
I tried row_number but worked only for couple of agrid's and didn't work for other.

I think you need to build two queries, one for table 1 and one for table 2, then union them together using the row_number and the header value to order them.
create table table1 (header bit, [type] char(1), agrid int, inno int, stallno int);
create table table2 (header bit, [type] char(1), agrid int, inno int, stallno int);
insert into table1 (header, [type], agrid, inno, stallno)
values
(0, 'D', 14, 001, 000),
(0, 'D', 14, 008, 000),
(0, 'N', 14, 008, 000);
insert into table2 (header, [type], agrid, inno, stallno)
values
(1, null, 14, 001, 000),
(1, null, 14, 008, 000);
select t1.*, row_number() over (order by t1.type, t1.inno) rn
from table1 t1
union all
select t2.*, row_number() over (order by t1.type, t1.inno) rn
from table1 t1
inner join table2 t2 on t2.inno = t1.inno
order by rn, header;
Returns
header
type
agrid
inno
stallno
rn
0
D
14
1
0
1
1
null
14
1
0
1
0
D
14
8
0
2
1
null
14
8
0
2
0
N
14
8
0
3
1
null
14
8
0
3
fiddle
Note: For future reference, please take the time to ensure your question is clear and easy to understand. And please create the DDL+DML (as I have shown here) so that people who wish to answer don't have to type your sample data in.

Related

SQL - Sum values when there is null

I have the following table:
RowID Column1 Column2
1 3 2
2 5 2
3 2 9
4 5 NULL
5 8 NULL
6 9 3
7 1 NULL
I need first row of Column1 to Sum every time there is a NULL value in Column2. And it would continue the logic down the rows.
So, the result should look like:
RowID Column1 Column2
1 3 2
2 5 2
3 15 9
4 5 NULL
5 8 NULL
6 10 3
7 1 NULL
Notice Row 3 summed 2+5+8 =15 and Row 6 summed 9+1 =10. So, basically the row prior to Null value in Column2 summed the values in column1 until there was no more NULL values in column2. Then it resumed in row 6 where the next value was NULL.
This will do it. I have set up the data in a table variable for demo.
declare #t table(RowID int, C1 int, C2 int)
insert #t values (1, 3, 2)
,(2, 5, 2)
,(3, 2, 9)
,(4, 5, NULL)
,(5, 8, NULL)
,(6, 9, 3)
,(7, 1, NULL)
select RowID, sum(C1), max(C2)
from (
select RowID, C1, C2 from #t
union all
select T1.RowID, T2.C1, null
from #t t1
join #t t2 on t2.RowID>t1.RowID and t2.C2 is null
and not exists(
select * from #t t3
where t3.RowID>t1.RowID and t3.c2 is not null and t3.RowID<t2.RowID
)
where T1.C2 is not null
) q group by RowID
Result:
RowID C1 C2
1 3 2
2 5 2
3 15 9
4 5 NULL
5 8 NULL
6 10 3
7 1 NULL
I got it. You need to look at the rows in reverse order, assigning the NULL values to the value before them.
The idea is to assign a group to the rows to sum. This is the number of non-NULL values following the row. With this, you can then use a window function to aggregate:
select t.*,
(case when c2 is null then c1
else sum(c1) over (partition by grp)
end) as new_c1
from (select t.*, count(c2) over (order by rowid rows between 1 following and unbounded following) as grp
from t
) t
order by rowid;
Here is a db<>fiddle.

Distribute values to several rows in SQL Server

I need help with SQL Server on how to distribute a row value to several rows with the same id. To illustrate,
Id = ProductInventoryCode
Qty = QuantityInStock
ForDistribution:
Id | Qty | TotalNoOfBranchesWithId
---+--------+-------------------------
1 | 40 | 2
2 | 33 | 3
3 | 21 | 2
A table that will receive the distributed values
Id | BranchCode | Qty | QtyFromForDistributionTable
-------------------------------------------------------
1 101 13 20
1 102 8 20
2 101 10 11
2 102 2 10
2 103 3 12
3 101 1 11
3 102 12 10
As much as possible the distribution should be near equal for each id and branches.
I got something like below, but somewhat got confused and lost path.
with rs as
(
select
r.*, cume.cumequantity,
coalesce(s.shipped, 0) AS shipped
from
tmpForDistribution r
cross apply
(SELECT SUM([QuantityInStock]) AS cumequantity
FROM tmpForDistribution r2
WHERE r2.ProductInventoryCode = r.ProductInventoryCode) cume
left join
(SELECT ProductInventoryCode, COUNT(ProductInventoryCode) AS shipped
FROM tmpDistributed s
GROUP BY s.ProductInventoryCode) s ON r.ProductInventoryCode = s.ProductInventoryCode
)
select
rs.ProductInventoryCode, rs.cumequantity, rs.QuantityInStock,
***"how to distribute"***
from rs
I'm currently using SQL Server 2008
Here's a sample screen output
The upper result is 145 Branches, below we use to distribute the ForDistributionQty field which is 3130, I am ending up with a fraction (DistVal = 21.586) which is not correct for this problem, it should be a whole number such as 21, however, if its just 21, then 21 x 145 is just 3045 which is shy of 85 units.
Here we distribute the values, and then make a final "adjustment" to the record which has the largest quantity (arbitrary). But at the end of the day, the math works and the distributed values are square.
Note: Not sure why in your sample why ID 2 did not get an even distribution
Declare #Table table (Id int,BranchCode int,Qty int)
Insert Into #Table values
(1, 101, 13),
(1, 102, 8),
(2, 101, 10),
(2, 102, 2),
(2, 103, 3),
(3, 101, 1),
(3, 102, 12)
Declare #Dist table (ID int,Qty int)
Insert Into #Dist values
(1,40),
(2,33),
(3,49)
;with cte0 as (
Select A.*
,ToDist = cast(D.Qty as int)
,DistVal = cast(D.Qty as int)/C.Cnt
,RN = Row_Number() over (Partition By A.ID Order By cast(D.Qty as int)/C.Cnt Desc,A.Qty Desc)
From #Table A
Join (Select ID,Cnt=count(*) from #Table Group By ID) C on A.ID=C.ID
Join #Dist D on A.ID=D.ID )
, cte1 as (
Select ID,AdjVal=Sum(DistVal)-max(ToDist) From cte0 Group By ID
)
Select A.ID
,A.BranchCode
,A.Qty
,DistVal = DistVal - case when A.RN<=abs(AdjVal) then 1*sign(AdjVal) else 0 end
From cte0 A
Join cte1 B on (A.ID=B.Id)
Order By 1,2
Returns
ID BranchCode Qty DistVal
1 101 13 20
1 102 8 20
2 101 10 11
2 102 2 11
2 103 3 11
3 101 1 24
3 102 12 25
If you can tolerate decimal values, a subquery seems to give a better query plan (tested on SQL 2014, with some sensible keys in place, this avoids a table spool and some additional index scans):
Declare #Table table (Id int,BranchCode int,Qty int, primary key(id, branchcode))
Insert Into #Table values
(1, 101, 13),
(1, 102, 8),
(2, 101, 10),
(2, 102, 2),
(2, 103, 3),
(3, 101, 1),
(3, 102, 12)
Declare #Dist table (ID int primary key,Qty int)
Insert Into #Dist values
(1,40),
(2,33),
(3,21)
SELECT
t.id
,t.BranchCode
,t.Qty
,(d.Qty / CAST((SELECT COUNT(*) as cnt FROM #table t2 where t.id = t2.id) AS decimal(10,2))) as DistributedQty
FROM #Table t
INNER JOIN #Dist d
ON d.id = t.Id
outputs:
Id BranchCode Qty DistributedQty
1 101 13 20.00000000000
1 102 82 20.00000000000
2 101 10 11.00000000000
2 102 21 11.00000000000
2 103 31 11.00000000000
3 101 11 10.50000000000
3 102 12 10.50000000000
If you need DistributedQty to be an int and retain remainders then I can't think of a better solution than #John Cappelletti's, noting that uneven quantities may not be as exactly even as you might hope (e.g. 32 distributed by three would result in a 12/10/10 distribution instead of an 11/11/10 distribution).

SQL query to return table with if exists retun price 2 else return price 1

I need help making a query to show the folowing result.
Supose I have tables:
Table 1
ProductId Description
1 Banana
2 Apple
3 Melon
4 Orange
Table 2
ProductId PriceNumber Price
1 1 86
1 2 55
2 1 58
3 1 99
3 3 66
4 1 87
4 2 78
I need to show PriceNumber = 2 and if it doesn't exists show PriceNumber = 1
Wanted result:
ProductId Description PriceNum Price
1 Banana 2 55
2 Apple 1 58
3 Melon 1 99
4 Orange 2 78
Thank you!
Here's the setup of the tables:
CREATE TABLE Table1
(`ProductId` int, `Description` varchar(6))
;
INSERT INTO Table1
(`ProductId`, `Description`)
VALUES
(1, 'Banana'),
(2, 'Apple'),
(3, 'Melon'),
(4, 'Orange')
;
CREATE TABLE Table2
(`ProductId` int, `PriceNumber` int, `Price` varchar(5))
;
INSERT INTO Table2
(`ProductId`, `PriceNumber`, `Price`)
VALUES
(1, 1, '7,86'),
(1, 2, '3,55'),
(2, 1, '10,58'),
(3, 1, '2,99'),
(4, 1, '9,87'),
(4, 2, '6,78')
;
Here's the actual answer in code:
SELECT distinct(Table2.ProductId),
Description,
PriceNumber,
Price
FROM Table2
INNER JOIN Table1
ON Table1.ProductId = Table2.ProductId
WHERE (PriceNumber = 2) OR
(
(Table2.ProductId not in (
SELECT ProductId
FROM Table2
WHERE PriceNumber = 2
)
)
AND
(PriceNumber = 1)
)
Here's a link to a sqlfiddle where you can play with the code:
http://sqlfiddle.com/#!9/234ab/4/0

MS SQL Set Group ID Without Looping

I would like create a query in MS-SQL to make a column containing an incrementing group number.
This is how I want my data to return:
Column 1 | Column 2 | Column 3
------------------------------
I | 1 | 1
O | 2 | 2
O | 2 | 3
I | 3 | 4
O | 4 | 5
O | 4 | 6
O | 4 | 7
O | 4 | 8
I | 5 | 9
O | 6 | 10
Column 1 is the I and O meaning In and Out.
Column 2 is the row Group (this should increment when Column 1 changes).
Column 3 is the Row-number.
So how can I write my query so that Column 2 increments every time Column 1 changes?
Firstly, to perform this kind of operation you need some column that can identify the order of the rows. If you have a column that determines this order, an identity column for example, it can be used to do something like this:
Runnable sample:
CREATE TABLE #Groups
(
id INT IDENTITY(1, 1) , -- added identity to provide order
Column1 VARCHAR(1)
)
INSERT INTO #Groups
( Column1 )
VALUES ( 'I' ),
( 'O' ),
( 'O' ),
( 'I' ),
( 'O' ),
( 'O' ),
( 'O' ),
( 'O' ),
( 'I' ),
( 'O' );
;
WITH cte
AS ( SELECT id ,
Column1 ,
1 AS Column2
FROM #Groups
WHERE id = 1
UNION ALL
SELECT g.id ,
g.Column1 ,
CASE WHEN g.Column1 = cte.Column1 THEN cte.Column2
ELSE cte.Column2 + 1
END AS Column2
FROM #Groups g
INNER JOIN cte ON cte.id + 1 = g.id
)
SELECT *
FROM cte
OPTION (MAXRECURSION 0) -- required to allow for more than 100 recursions
DROP TABLE #Groups
This code effectively loops through the records, comparing each row to the next and incrementing the value of Column2 if the value in Column1 changes.
If you don't have an identity column, then you might consider adding one.
Credit #AeroX:
With 30K records, the last line: OPTION (MAXRECURSION 0) is required to override the default of 100 recursions when using a Common Table Expression (CTE). Setting it to 0, means that it isn't limited.
This will work if you have sqlserver 2012+
DECLARE #t table(col1 char(1), col3 int identity(1,1))
INSERT #t values
('I'), ('O'), ('O'), ('I'), ('O'), ('O'), ('O'), ('O'), ('I'), ('O')
;WITH CTE AS
(
SELECT
case when lag(col1) over (order by col3) = col1
then 0 else 1 end increase,
col1,
col3
FROM #t
)
SELECT
col1,
sum(increase) over (order by col3) col2,
col3
FROM CTE
Result:
col1 col2 col3
I 1 1
O 2 2
O 2 3
I 3 4
O 4 5
O 4 6
O 4 7
O 4 8
I 5 9
O 6 10

How to Mapping Columns in a Self-Join table!

I have a parent/child table and want to update Its PK and FK to new values. the problem is that oldParent Ids Should Sync with new ones matching with Old Ids. so:
I have this data as a temp table:
OldID | OldParentID | NewID | NewParentID
1 0 10 NULL
2 0 11 NULL
3 2 13 NULL
4 3 14 NULL
and I Need Update NewParentID as Follows:
OldID | OldParentID | NewID | NewParentID
1 0 10 0
2 0 11 0
3 2 13 11
4 3 14 13
declare #T table
(
OldID int,
OldParentID int,
NewID int,
NewParentID int
)
insert into #T
select 1, 0, 10, null union all
select 2, 0, 11, null union all
select 3, 2, 13, null union all
select 4, 3, 14, null
update T1
set T1.NewParentID = coalesce(T2.NewID, 0)
from #T as T1
left outer join #T as T2
on T1.OldParentID = T2.OldID