SQL Server How to insert when not exist? - sql

I have two tables, one is called Invoices and another is called Records.
CREATE TABLE Invoices
(
InvoiceNum INT NOT NULL,
Amount DECIMAL,
RecordPK UNIQUEIDENTIFIER NOT NULL
)
CREATE TABLE Records(
RecordPK UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
StartNum INT NOT NULL,
NextNum INT NOT NULL,
MaxNum INT NOT NULL,
InvPrefix VARCHAR(2) NOT NULL
)
The records table will record the invoice start number, how many invoices we have created(NextNum) and how many invoices we can create(MaxNum).
For example, Assume we have several records in two tables.
Invoice Table:
InvoiceNum Amount RecordPk
1 19.00 EDFA0541-5583-4CDD-BDFF-21D6F6504522
2 50.00 EDFA0541-5583-4CDD-BDFF-21D6F6504522
3 3.00 EDFA0541-5583-4CDD-BDFF-21D6F6504522
10 1.00 D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9
11 99.00 D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9
12 13.00 D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9
Records Table:
RecordPk StartNum NextNum MaxNum Prefix
EDFA0541-5583-4CDD-BDFF-21D6F6504522 1 4 10 AA
D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9 10 13 14 AA
My question is when I search the invoice table with Prefix AA, how can I get the result like below, the InvoiceNum should reach the MaxNum, the Amount and RecordPK of not exist rows should left blank, the Remark column should fill with Blank.
InvoiceNum Amount RecordPk Remark
1 19.00 EDFA0541-5583-4CDD-BDFF-21D6F6504522
2 50.00 EDFA0541-5583-4CDD-BDFF-21D6F6504522
3 3.00 EDFA0541-5583-4CDD-BDFF-21D6F6504522
4 Blank
5 Blank
6 Blank
7 Blank
8 Blank
9 Blank
10 1.00 D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9
11 99.00 D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9
12 13.00 D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9
13 Blank
14 Blank

You need to generate a table with numbers to cover the range of numbers that you need (for each row in Records table, from StartNum to MaxNum). You can do this for example, by selecting from some existing table with enough rows and using ROW_NUMBER window function. Then filter this sequence to include only the numbers you need. Left join the Invoices table to show the data for the corresponding invoice and use IIF function to check is there invoice with this number or not.
declare #Invoices table(InvoiceNum INT NOT NULL, Amount DECIMAL, RecordPK UNIQUEIDENTIFIER NOT NULL)
declare #Records table(RecordPK UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, StartNum INT NOT NULL, NextNum INT NOT NULL, MaxNum INT NOT NULL, InvPrefix VARCHAR(2) NOT NULL)
insert into #Invoices(InvoiceNum, Amount, RecordPk) values
(1 , 19.00, 'EDFA0541-5583-4CDD-BDFF-21D6F6504522'),
(2 , 50.00, 'EDFA0541-5583-4CDD-BDFF-21D6F6504522'),
(3 , 3.00 , 'EDFA0541-5583-4CDD-BDFF-21D6F6504522'),
(10, 1.00 , 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9'),
(11, 99.00, 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9'),
(12, 13.00, 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9')
insert into #Records(RecordPk, StartNum, NextNum, MaxNum, InvPrefix) values
('EDFA0541-5583-4CDD-BDFF-21D6F6504522', 1 , 4 , 10, 'AA'),
('D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9', 10, 13, 14, 'AA')
;with numbers as (select ROW_NUMBER() over(order by object_id) as No from sys.objects)
select
n.No as InvoiceNum
, inv.Amount
, inv.RecordPK
, IIF(inv.InvoiceNum is null, 'Blank', null) as Remark
from numbers n
left join #Invoices inv on n.No = inv.InvoiceNum
where exists(select * from #Records r where r.StartNum <= n.No and n.No <= r.MaxNum)

#Andrey Nikolov has it covered, however I've been working on this for the last 15 minutes so I thought I'd post it anyway.
Essentially an intermediary table should be used to count up the values you don't have, then in my version of this answer I've used a union query to generate the "Blank" value. I have not included the unique identifier for brevity but the application is the same.
if OBJECT_ID('tempdb..#invoice') is not null drop table #invoice;
if OBJECT_ID('tempdb..#rowcount') is not null drop table #rowcount;
create table #invoice
(
invoicenum int,
amount decimal
);
insert into #invoice (invoicenum, amount)
values
(1, 19.00),
(2, 50.00),
(3, 3.00),
(10, 1.00),
(11, 99.00),
(12, 13.00);
create table #rowcount
(
rownumber int
);
declare #max int = 1;
select #max=count(*) from #invoice;
declare #runs int = 1;
while #runs<=#max
begin
insert into #rowcount (rownumber)
values (#runs);
select #runs=#runs+1;
end
select invoicenum, cast(amount as nvarchar(25)) as amount from #invoice
union
select rownumber, 'BLANK' from #rowcount r left join #invoice i on
r.rownumber=i.invoicenum where i.invoicenum is null
order by invoicenum;
drop table #invoice, #rowcount;

You need a LEFT JOIN
SELECT I.*,
CASE WHEN I.InvoiceNum IS NULL THEN 'Blank' END Remark
FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14)) RC (InvoiceNum)
LEFT JOIN Invoices I
ON RC.InvoiceNum = I.InvoiceNum;
The value 1 is the StartNum and 14 is the MAX MaxNum.
I used VALUES cause the number is know, you can use a RecursiveCTE to generate the missing InvoiceNum then LEFT JOIN the CTE with your table.
Demo

I will do it this way:
IF OBJECT_ID('tempdb..#Invoices') IS NOT NULL DROP TABLE #Invoices
CREATE TABLE #Invoices
(
InvoiceNum INT NOT NULL,
Amount DECIMAL,
RecordPK UNIQUEIDENTIFIER NOT NULL
)
IF OBJECT_ID('tempdb..#Records') IS NOT NULL DROP TABLE #Records
CREATE TABLE #Records(
RecordPK UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
StartNum INT NOT NULL,
NextNum INT NOT NULL,
MaxNum INT NOT NULL,
InvPrefix VARCHAR(2) NOT NULL
)
INSERT INTO #Invoices
SELECT 1, 19.00, 'EDFA0541-5583-4CDD-BDFF-21D6F6504522'
UNION SELECT 2 , 50.00, 'EDFA0541-5583-4CDD-BDFF-21D6F6504522'
UNION SELECT 3 , 3.00 , 'EDFA0541-5583-4CDD-BDFF-21D6F6504522'
UNION SELECT 10 , 1.00 , 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9'
UNION SELECT 11 , 99.00, 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9'
UNION SELECT 12 , 13.00, 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9'
INSERT INTO #Records
SELECT 'EDFA0541-5583-4CDD-BDFF-21D6F6504522', 1, 4, 10, 'AA'
UNION SELECT 'D64EFF0E-65D5-467E-8C82-BFBB6A24AAC9', 10, 13, 14, 'AA'
DECLARE #MAX_NUM INT = (SELECT MAX(MaxNum) FROM #Records)
DECLARE #TEMP_INV TABLE (InvoiceNum INT)
INSERT INTO #TEMP_INV
SELECT Num
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY object_id) AS Num FROM sys.objects
) A
WHERE Num <= #MAX_NUM
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL DROP TABLE #TEMP
SELECT I.InvoiceNum, I.Amount, I.RecordPK
INTO #TEMP
FROM #Invoices I
INNER JOIN #Records R
ON I.RecordPK = R.RecordPK
WHERE R.InvPrefix = 'AA'
SELECT A.InvoiceNum, B.Amount, B.RecordPK, CASE WHEN B.InvoiceNum IS NULL THEN 'BLANK' END AS Remark
FROM #TEMP_INV A
LEFT JOIN #TEMP B
ON A.InvoiceNum = B.InvoiceNum

Related

Get the id of the last record in the data SQL Server

I am trying to get the last ID from at least 4 child-parent relationships between ID's and sum all related ID's quantity. I have tried below -
declare #test table (ID int not null, P_ID int null, Qty int not null)
insert into #test(ID, P_ID, Qty) values
(1 , 11 , 1),
(2 , null, 3),
(11, 21 , 2),
(21, 31 , 1),
(31, null, 3),
(12, null, 4)
select
COALESCE(T2.ID,T1.ID) as ID,
MAX(CASE WHEN T1.P_ID is not null then T1.ID END) as OldID,
SUM(Qty) as Qty
from
#test T1
left join
(select ID from #test
GROUP By ID) T2
on T2.ID = T1.P_ID
group by
COALESCE(T2.ID, T1.ID)
I am getting output -
ID OldID Qty
2 NULL 3
11 1 1
12 NULL 4
21 11 1
31 21 2
But I want my output will be like this where all ID's with no Parent ID in the first row then all previous ID's will show and SUM all relevant ID's quantity -
ID OldID3 OldID2 OldID1 Qty
2 3
12 4
31 21 11 1 7
Could someone please help me to achieve this.
Thanks in advance
Hopefully, this helps you. I have not tested it thoroughly, so apologies for any bugs.
I'm using a Common Table Expression to get the hierarchy information, then using a dynamic SQL I extract the desired number of previous IDs.
DECLARE #test TABLE (ID INT NOT NULL, P_ID INT NULL, Qty INT NOT NULL);
INSERT INTO #test(ID, P_ID, Qty) VALUES
(1 , 11 , 1),
(2 , null, 3),
(11, 21 , 2),
(21, 31 , 1),
(31, null, 3),
(12, null, 4);
IF (OBJECT_ID('tempdb..#hierarchy') IS NOT NULL)
DROP TABLE #hierarchy;
CREATE TABLE #hierarchy (
RootID INT NOT NULL, ID INT NOT NULL, [Qty] INT NOT NULL, SeqIndex INT NOT NULL
);
;WITH hierarchy AS (
SELECT ID, P_ID, Qty, ID [RootID], 0 [SeqIndex]
FROM #test
WHERE P_ID IS NULL
UNION ALL
SELECT child.ID, child.P_ID, child.Qty, parent.RootID, parent.SeqIndex + 1 [SeqIndex]
FROM #test child
JOIN hierarchy parent ON parent.ID=child.P_ID
)
INSERT #hierarchy
SELECT RootID, ID, Qty, SeqIndex
FROM hierarchy;
DECLARE
#DEPTH INT = 3,
#maxSeqIndex INT = (SELECT MAX(SeqIndex) FROM #hierarchy);
IF (#DEPTH = 0)
SELECT RootID, SUM(Qty) [Qty]
FROM #hierarchy
GROUP BY RootID;
ELSE IF (#DEPTH > #maxSeqIndex)
SELECT NULL
ELSE BEGIN
DECLARE #SQL NVARCHAR(MAX) = N'
SELECT
RootID,
';
DECLARE #idx INT = 1;
WHILE #idx <= #DEPTH BEGIN
SET #SQL += N'
(SELECT ID FROM #hierarchy i WHERE i.RootID=o.RootID AND SeqIndex='+CAST(#idx as nvarchar(10))+N') [OldID'+CAST(#maxSeqIndex-#idx+1 as nvarchar(10))+N'],';
SET #idx += 1;
END
SET #SQL += N'
SUM(Qty) [Qty]
FROM #hierarchy o
GROUP BY RootID;';
EXEC sp_executesql #SQL
END
Of course, the dynamic script could be replaced with a hard-coded SQL if that is OK for you.
Note: performance has not been considered

Recursive CTE to find all records SQL Server

I am having below table structure. I want to write a recursive cte to get the bottom most table result.
Highly appreciate your help.
CREATE TABLE Jobcard (
jobcard_id INT NOT NULL PRIMARY KEY,
jobcard_name varchar(20) NOT NULL,
);
CREATE TABLE Vehicle (
vehicle_id INT NOT NULL PRIMARY KEY,
vehicle_name varchar(20) NOT NULL
);
CREATE TABLE Jobacard_vehicle (
jobcard_id INT NOT NULL,
vehicle_id INT NOT NULL
);
INSERT INTO Jobcard (jobcard_id, jobcard_name) VALUES
(1, 'Job1'),(2, 'Job2'),(3, 'Job3'),
(4, 'Job4'),(5, 'Job5'),(6, 'Job6'),
(7, 'Job7'),(8, 'Job8'),(9, 'Job9');
INSERT INTO Vehicle (vehicle_id, vehicle_name) VALUES
(1, 'Vehicle1'),(2, 'Vehicle2'),(3, 'Vehicle3'),
(4, 'Vehicle4'),(5, 'Vehicle5'),(6, 'Vehicle6');
INSERT INTO Jobacard_vehicle (jobcard_id, vehicle_id) VALUES
(3, 1),(4, 2),(5, 3),
(9, 6),(7, 2),(5, 4),
(8, 4),(6, 1),(3, 5);
jobcard_id, vehicle_id
--------------------------
3 1
4 2
5 3
9 6
7 2
5 4
8 4
6 1
3 5
I want to get this result from Jobacard_vehicle table when I pass the vehicle id as 3 as
vehicle id 3 is having jobcard 5
jobcard 5 is having vehicle 4
again vehicle 4 referred to jobcard 8
means all the jobcard refered by 3 or its counter part jobcards vehicles
jobcard_id, vehicle_id
--------------------------
5 3
5 4
8 4
Thank you.
Try to save full path and check it in the next recursion
DECLARE #startVehicleID int=3
;WITH vehCTE AS(
SELECT jobcard_id,vehicle_id,CAST(CONCAT('(',jobcard_id,',',vehicle_id,')') AS varchar(MAX)) [path]
FROM Jobacard_vehicle
WHERE vehicle_id=#startVehicleID
UNION ALL
SELECT v.jobcard_id,v.vehicle_id,c.[path]+CONCAT('(',v.jobcard_id,',',v.vehicle_id,')')
FROM Jobacard_vehicle v
JOIN vehCTE c ON (v.jobcard_id=c.jobcard_id OR v.vehicle_id=c.vehicle_id) AND CHARINDEX(CONCAT('(',v.jobcard_id,',',v.vehicle_id,')'),c.[path])=0
)
SELECT *
FROM vehCTE
ORDER BY [path]
If you need to check Job->Vehicle->Job->Vehicle->... then I think you can use the following
DECLARE #startVehicleID int=3
;WITH vehCTE AS(
SELECT
jobcard_id,
vehicle_id,
CAST(CONCAT('(',jobcard_id,',',vehicle_id,')') AS varchar(MAX)) [path],
1 NextIsJob
FROM Jobacard_vehicle
WHERE vehicle_id=#startVehicleID
UNION ALL
SELECT
v.jobcard_id,
v.vehicle_id,
c.[path]+CONCAT('(',v.jobcard_id,',',v.vehicle_id,')'),
IIF(c.NextIsJob=1,0,1)
FROM Jobacard_vehicle v
JOIN vehCTE c ON ((c.NextIsJob=1 AND v.jobcard_id=c.jobcard_id) OR (c.NextIsJob=0 AND v.vehicle_id=c.vehicle_id)) AND CHARINDEX(CONCAT('(',v.jobcard_id,',',v.vehicle_id,')'),c.[path])=0
)
SELECT *
FROM vehCTE
ORDER BY [path]

SQL: Upsert and get the old and the new values

I have the following table Items:
Id MemberId MemberGuid ExpiryYear Hash
---------------------------------------------------------------------------
1 1 Guid1 2017 Hash1
2 1 Guid2 2018 Hash2
3 2 Guid3 2020 Hash3
4 2 Guid4 2017 Hash1
I need to copy the items from a member to another (not just to update MemberId, to insert a new record). The rule is: if I want to migrate all the items from a member to another, I will have to check that that item does not exists in the new member.
For example, if I want to move the items from member 1 to member 2, I will move only item with id 2, because I already have an item at member 2 with the same hash and with the same expiry year (this are the columns that I need to check before inserting the new items).
How to write a query that migrates only the non-existing items from a member to another and get the old id and the new id of the records? Somehow with an upsert?
You can as the below:
-- MOCK DATA
DECLARE #Tbl TABLE
(
Id INT IDENTITY NOT NULL PRIMARY KEY,
MemberId INT,
MemberGuid CHAR(5),
ExpiryYear CHAR(4),
Hash CHAR(5)
)
INSERT INTO #Tbl
VALUES
(1, 'Guid1', '2017', 'Hash1'),
(1, 'Guid2', '2018', 'Hash1'),
(2, 'Guid3', '2020', 'Hash3'),
(2, 'Guid4', '2017', 'Hash1')
-- MOCK DATA
-- Parameters
DECLARE #FromParam INT = 1
DECLARE #ToParam INT = 2
DECLARE #TmpTable TABLE (NewDataId INT, OldDataId INT)
MERGE #Tbl AS T
USING
(
SELECT * FROM #Tbl
WHERE MemberId = #FromParam
) AS F
ON T.Hash = F.Hash AND
T.ExpiryYear = F.ExpiryYear AND
T.MemberId = #ToParam
WHEN NOT MATCHED THEN
INSERT ( MemberId, MemberGuid, ExpiryYear, Hash)
VALUES ( #ToParam, F.MemberGuid, F.ExpiryYear, F.Hash)
OUTPUT inserted.Id, F.Id INTO #TmpTable;
SELECT * FROM #TmpTable
Step 1:
Get in cursor all the data of member 1
Step 2:
While moving through cursor.
Begin
select hash, expirydate from items where memberid=2 and hash=member1.hash and expirydate=member1.expirydate
Step 3
If above brings any result, do not insert.
else insert.
Hope this helps
Note: this is not actual code. I am providing you just steps based on which you can write sql.
Actually you just need an insert. When ExpiryYear and Hash matched you don't wanna do anything. You just wanna insert from source to target where those columns doesn't match. You can do that with Merge or Insert.
CREATE TABLE YourTable
(
Oldid INT,
OldMemberId INT,
Id INT,
MemberId INT,
MemberGuid CHAR(5),
ExpiryYear CHAR(4),
Hash CHAR(5)
)
INSERT INTO YourTable VALUES
(null, null, 1, 1, 'Guid1', '2017', 'Hash1'),
(null, null, 2, 1, 'Guid2', '2018', 'Hash2'),
(null, null, 3, 2, 'Guid3', '2020', 'Hash3'),
(null, null, 4, 2, 'Guid4', '2017', 'Hash1')
DECLARE #SourceMemberID AS INT = 1
DECLARE #TargetMemberID AS INT = 2
MERGE [YourTable] AS t
USING
(
SELECT * FROM [YourTable]
WHERE MemberId = #SourceMemberID
) AS s
ON t.Hash = s.Hash AND t.ExpiryYear = s.ExpiryYear AND t.MemberId = #TargetMemberID
WHEN NOT MATCHED THEN
INSERT(Oldid, OldMemberId, Id, MemberId, MemberGuid, ExpiryYear, Hash) VALUES (s.Id, s.MemberId, (SELECT MAX(Id) + 1 FROM [YourTable]), #TargetMemberID, s.MemberGuid, s.ExpiryYear, s.Hash);
SELECT * FROM YourTable
DROP TABLE YourTable
/* Output:
Oldid OldMemberId Id MemberId MemberGuid ExpiryYear Hash
-----------------------------------------------------------------
NULL NULL 1 1 Guid1 2017 Hash1
NULL NULL 2 1 Guid2 2018 Hash2
NULL NULL 3 2 Guid3 2020 Hash3
NULL NULL 4 2 Guid4 2017 Hash1
2 1 5 2 Guid2 2018 Hash2
If you just want to select then do as following
SELECT null AS OldID, null AS OldMemberID, Id, MemberId, MemberGuid, ExpiryYear, Hash FROM YourTable
UNION ALL
SELECT A.Id AS OldID, A.MemberId AS OldMemberID, (SELECT MAX(Id) + 1 FROM YourTable) AS Id, #TargetMemberID AS MemberId, A.MemberGuid, A.ExpiryYear, A.Hash
FROM YourTable A
LEFT JOIN
(
SELECT * FROM YourTable WHERE MemberId = #TargetMemberID
) B ON A.ExpiryYear = B.ExpiryYear AND A.Hash = B.Hash
WHERE A.MemberId = #SourceMemberID AND B.Id IS NULL

Price calculation based on the unit entered

Table Schema:
CREATE TABLE [dbo].[TblPriceDetails](
[PriceID] [int] IDENTITY(1,1) NOT NULL,
[VID] [int] NOT NULL,
TypeID int not null,
[RangeStart] [decimal](18, 3) NOT NULL,
[RangeEnd] [decimal](18, 3) NOT NULL,
[Price] [decimal](18, 2) NOT NULL,
[ExtraLoad] [decimal](18, 3) NULL,
[ExtraPrice] [decimal](18, 2) NULL
)
GO
Sample Data
Insert into dbo.TblPriceDetails values (1,1, 0,0.250,10,0,0)
Insert into dbo.TblPriceDetails values (1,1, 0.251,0.500,15,0.500,15)
Insert into dbo.TblPriceDetails values (1,1, 3,5,40,1,25)
GO
Insert into dbo.TblPriceDetails values (1,2, 0,0.250,15,0,0)
Insert into dbo.TblPriceDetails values (1,2, 0.251,0.500,20,0.500,20)
Insert into dbo.TblPriceDetails values (1,2, 3,5,50,1,30)
GO
Expected Output:
For VID = 1 and TypeID = 1 and a given value 0.300
As the input unit falls between RangeStart 0.251 and RangeEnd 0.500
the resultant price will be 15
For VID = 1 and TypeID = 1 and a given value 0.600
As per the data until 0.500 the price is 15 and for every extraLoad
of upto 0.500 its another 15. So the final price will be 30
For VID = 1 and TypeID = 1 and given value 1.500
As per the data until 0.500 the price is 15. For every extra 0.500
its another 15, so for the remaining 1 unit it would be 15 * 2. The
final price will be 45
For VID = 1 and TypeID = 1 and given value 5.5
As per the data until 5.000 the price is 40. For every extra 1 unit its another 25, so the final price will be 65
Need help in writing a query for this. Unlike my other questions I don't have a query yet to show what I have come up with till now. As of now I am not able to frame a logic and come up with a generic query for this.
It looks like you are looking to calculate postage price. The trick is to join on the RangeStart of the next weight tier. LEAD will help you do that:
;WITH
AdjustedPriceDetails AS
(
SELECT VID, TypeID, RangeStart, RangeEnd, Price, ExtraLoad, ExtraPrice
, ISNULL(LEAD(RangeStart, 1) OVER (PARTITION BY VID, TypeID ORDER BY RangeStart), 1000000) AS NextRangeStart
FROM TblPriceDetails
)
SELECT T.*
, A.Price + IIF(T.Value <= A.RangeEnd, 0, CEILING((T.Value - A.RangeEnd) / A.ExtraLoad) * A.ExtraPrice)
AS FinalPrice
FROM #TestData T
INNER JOIN AdjustedPriceDetails A ON A.RangeStart <= T.Value AND T.Value < A.NextRangeStart
Explanation:
LEAD(RangeStart, 1) OVER (PARTITION BY VID, TypeID ORDER BY RangeStart) gets the RangeStart of the next row that has the same VID and TypeID
You will eventually reach the highest weight tier. So ISNULL(..., 1000000) make this tier appear to end at 1M. The 1M is just a stand-in for infinity.
Edit: if you want to make this work with SQL Server 2008, change the CTE:
;WITH
tmp AS
(
SELECT VID, TypeID, RangeStart, RangeEnd, Price, ExtraLoad, ExtraPrice
, ROW_NUMBER() OVER (PARTITION BY VID, TypeID ORDER BY RangeStart) AS RowNumber
FROM TblPriceDetails
),
AdjustedPriceDetails AS
(
SELECT T1.VID, T1.TypeID, T1.RangeStart, T1.RangeEnd, T1.Price, T1.ExtraLoad, T1.ExtraPrice
, ISNULL(T2.RangeStart, 1000000) AS NextRangeStart
FROM tmp T1
LEFT JOIN tmp T2 ON T1.VID = T2.VID AND T1.TypeId = T2.TypeID AND T1.RowNumber + 1 = T2.RowNumber
)
If you wonder what #TestData is (you may not need it)
CREATE TABLE #TestData
(
VID int
, TypeID int
, Value float
)
INSERT INTO #TestData
( VID, TypeID, Value)
VALUES ( 1, 1, 0.3 )
, ( 1, 1, 0.6 )
, ( 1, 1, 1.5 )
, ( 1, 1, 5.5 )

Subquery: how to retrieve the last non-zero value from a column?

Considering a table customerBalance with 3 columns: name, date and balance. Suppose a set of records like:
cus_name cus_date cus_balance
John 06/14/2011 1000
John 06/15/2011 500
John 06/16/2011 0
Mary 06/14/2011 3000
Mary 06/15/2011 2800
Mary 06/16/2011 0
How to create a SQL query which returns, for the date 6/16/2011 instead 0, the last non-zero value based on date (in sample, $500 for John and $2800 for Mary)?
I'm trying to do it using a subquery which uses Max function to retrieve the last date with non-zero value, but I didn't succeed. This example is quite "nonsensical", but I really need to do an operation like this in my dataset. Thanks!
Note: If you can specify the DB and version this query can be improved.
Try this:
SELECT *
FROM customers
WHERE (cus_name, cus_date)
IN
(
SELECT cus_name, MAX(cus_date)
FROM customers
WHERE cus_balance <> 0
GROUP BY cus_name
)
Update: Alternate version:
SELECT a.*
FROM customers a,
(
SELECT cus_name, MAX(cus_date)
FROM customers
WHERE cus_balance <> 0
GROUP BY cus_name
) b
WHERE a.cus_name = b.cus_name
AND a.cus_date = b.cus_date
Here it goes:
CREATE Table #temp
(
Cus_Name VARCHAR(200) NULL,
Cus_Date Char(8) NULL,
Cus_Balance INT NULL
)
INSERT INTO #temp VALUES ('John' , '20110614' ,1000 )
INSERT INTO #temp VALUES ('John' , '20110615' , 500 )
INSERT INTO #temp VALUES ('John' , '20110616' , 0 )
INSERT INTO #temp VALUES ('Mary' , '20110614' ,3000 )
INSERT INTO #temp VALUES ('Mary' , '20110615' ,2800 )
INSERT INTO #temp VALUES ('Mary' , '20110616' , 0 )
SELECT
T.Cus_Name ,
MIN(t.Cus_Balance)
FROM #temp t
WHERE t.Cus_Balance <>0
GROUP BY t.Cus_Name
DROP TABLE #temp