SQL query with SUM, ORDER BY, with limited values - tsql
I am having a troubles to get expected result.
I have successfully joined 2 tables (database is SQL Server), however I would like to have something more.
Excel - it's table which contains list of PartNumbers(GBC) with corresponding Quantity of this part needed for build.
I'm joining this Excel with my Inventory database to retrieve information about what I have, what is missing, what I need to purchase.
Current query:
string sqlCheck = #"SELECT e.GBC, e.Replaced, e.Description, Barcode, Location, Bookstand, Quantity, Buildneed, p.Quantity - e.Buildneed as Afterbuild FROM Parts p Right JOIN Excel e ON e.GBC = p.GBC ORDER BY GBC ASC, Quantity DESC";
Results as below image:
It is needed to use ALL duplicated GBC but in specific order.
First I need to take GBC(86911) with Quantity = 100, this should result in Afterbuild = 0.
But in column Buildneed I see that I need 768 in total, so next step would be to take GBC(86911) with quantity = 500, this should result in Afterbuild 0, and in this line I would like to see new column called Totals which will be equal to -168 (which will mean I need to buy 168 pcs of this part).
I can have many same parts with different quantities
I always want to start from lowest quantity on particular duplicated GBC
This should not show me more GBC if Buildneed value will be exceeded
Expected output as on below image:
I have added column 'UseInOrder' - it is not neccessery but would be awesome if its possible also, it will point me that I will need to use all parts from each in that order.
As suggested, below table definitions:
CREATE TABLE [dbo].[Excel] (
[GBC] INT NULL,
[Description] VARCHAR (50) NULL,
[Buildneed] INT NULL,
[Replaced] VARCHAR (50) NULL
);
CREATE TABLE [dbo].[Parts] (
[Barcode] INT IDENTITY (201900001, 1) NOT NULL,
[GBC] INT NULL,
[Description] VARCHAR (50) NULL,
[Location] VARCHAR (50) NULL,
[Bookstand] VARCHAR (50) NULL,
[Value] VARCHAR (50) NULL,
[Quantity] INT NULL,
[MQuantity] INT NULL,
[Manufacturer1] VARCHAR (50) NULL,
[MPN1] VARCHAR (50) NULL,
[Manufacturer2] VARCHAR (50) NULL,
[MPN2] VARCHAR (50) NULL,
[Manufacturer3] VARCHAR (50) NULL,
[MPN3] VARCHAR (50) NULL,
CONSTRAINT [PK_Parts] PRIMARY KEY CLUSTERED ([Barcode] ASC)
);
EDIT Sample data
declare #tblParts table(
GBC int,
Barcode varchar(256),
[Location] varchar(256),
Quantity int
)
declare #tblPartsUsed table(
GBC int,
Replaced varchar(1) default '',
[Description] varchar(50),
Buildneed int
)
insert into #tblParts (GBC,[Description], Barcode, [Location], Quantity)
select 86911, 'CAP_CER,10nF,0603,10%,100V,X7R' ,201901200, 'JD-01/ 14' ,500
union all
select 86911, 'CAP_CER,10nF,0603,10%,100V,X7R' ,201901166, 'ESB-03' ,100
union all
select 99529, 'DIO_ZENR,5,6V,2%,MM3Z5V6ST1G,SOD323' ,201901024, 'ESB-01' ,100
union all
select 128082, 'CAP_CER,100nF,0603,10%,50V,X7R, poly' ,201901120, 'JD-01/ 3' ,500
union all
select 128082, 'CAP_CER,100nF,0603,10%,50V,X7R, poly' ,201901121, 'JD-01/ 3' ,500
union all
select 168078, 'CAP_CER,470nF,0805,10%,50V,X7R' ,201901207, 'JD-01/ 19' ,170
union all
select 168078, 'CAP_CER,470nF,0805,10%,50V,X7R' ,201901152, 'ESB-03' ,140
union all
select 196881, 'BJT,C,SMBT3946DW1T1G,SOT363' ,201901085, 'ESB-02' ,100
union all
select 199296, 'BJT_DIG,C,SMUN5311DW1T1G,SOT363' ,201901083, 'ESB-02' ,100
union all
select 207735, 'DIO_LED, NFSA123DT' ,201902132, 'KRK' ,10
insert into #tblPartsUsed(GBC, [Description], Buildneed)
select 71744, 'RES_TF,10k,0402,1%,0,1W,100PPM/C' ,192
union all
select 71746, 'RES_TF,10k,0603,1%,0,1W,100PPM/C' ,168
union all
select 76527, 'CAP_CER,10nF,0402,10%,50V,X7R' ,288
union all
select 86911, 'CAP_CER,10nF,0603,10%,100V,X7R' ,1464
union all
select 92854, 'RES_TF,30k,0603,1%,0,1W,100PPM/C' ,72
union all
select 93018, 'RES_TF,68k,0603,1%,0,1W,100PPM/C' ,72
union all
select 95241, 'RES_TF,2k2,0402,1%,0,1W,100PPM/C' ,192
union all
select 95549, 'RES_TF,47k,0603,1%,0,1W,100PPM/C' ,72
union all
select 99529, 'DIO_ZENR,5,6V,2%,MM3Z5V6ST1G,SOD323' ,72
union all
select 112117, 'RES_TF,2k2,0603,1%,0,1W,100PPM/C' ,96
union all
select 126486, 'RES_TF,0R,0603' ,24
union all
select 128082, 'CAP_CER,100nF,0603,10%,50V,X7R, poly' ,72
union all
select 168078, 'CAP_CER,470nF,0805,10%,50V,X7R' ,72
union all
select 196200, 'BJT_DIG,N,PDTC114EU,SOT323' ,72
union all
select 196881, 'BJT,C,SMBT3946DW1T1G,SOT363' ,144
union all
select 199296, 'BJT_DIG,C,SMUN5311DW1T1G,SOT363' ,504
union all
select 199302, 'RES_TF,100R,0603,10%,0,1W,200PPM/C' ,72
union all
select 202047, 'UNI,N,PMPB215ENEA,DFN2020MD-6' ,72
union all
select 202054, 'DIO_LED,SPMWHT346EA3' ,648
union all
select 203509, 'CONN_HEADER,MOLEX,5023521100' ,24
union all
select 207735, 'DIO_LED, NFSA123DT' ,648
union all
select 207843, 'Thermistor,10k,0603,1%,NTC' ,24
union all
select 208252, 'FOOTPRINT_BOARD-IN,MOLEX,350220011' ,48
union all
select 212145, 'DIO_SIGN,200V,250mA,50nS,BAV21WSQ-7-F,SOD323' ,72
Based on this date, output should be like on below image of table:
Try this
EDIT2 (after change initial data)
SELECT distinct u.GBC,p2.Replaced, p2.Description, IIF(u.Afterbuild>0, MIN(p1.Barcode) OVER(PARTITION BY u.GBC), p1.Barcode) as Barcode, p1.Location, u.Quantity, u.Buildneed,
CASE
when (u.Total <0 AND p1.Barcode is NULL) OR (SUM(u.Quantity) OVER(PARTITION BY u.GBC) - u.Buildneed)>0 then u.Afterbuild
else 0
end
as Afterbuild,
CASE
when u.Total <0 AND p1.Barcode is NULL then ABS(u.Total)
when (SUM(u.Quantity) OVER(PARTITION BY u.GBC) - u.Buildneed)>0 then u.Total
else u.Total
END as Total FROM
(
SELECT distinct b.GBC, b.Quantity, b.Buildneed,
case
when b.Total <0 then null
when b.Total >=0 AND MIN(b.Quantity) OVER(PARTITION BY b.GBC) - b.Buildneed < 0 AND b.Quantity = MAX(b.Quantity) OVER(PARTITION BY b.GBC) then SUM(b.Quantity) OVER(PARTITION BY b.GBC) - b.Buildneed
when b.Total >=0 AND MIN(b.Quantity) OVER(PARTITION BY b.GBC) - b.Buildneed >= 0 AND b.Quantity = MIN(b.Quantity) OVER(PARTITION BY b.GBC) then MIN(b.Quantity) OVER(PARTITION BY b.GBC) - b.Buildneed
END AS Afterbuild,
case
when b.Total >=0 then null
when b.Total <0 AND b.RowNumber = MAX(b.RowNumber) OVER(PARTITION BY b.GBC) then b.Total
END AS Total
FROM
(
select r.GBC, r.Quantity, r.Buildneed,
(SUM(r.Quantity) OVER(PARTITION BY r.GBC)) - r.Buildneed as Total, ROW_NUMBER() OVER(order by r.GBC, r.Quantity) as RowNumber
from
(
SELECT e.GBC, ISNULL(Quantity,0) as Quantity, ISNULL(Buildneed,0) as Buildneed FROM #tblParts p RIGHT JOIN #tblPartsUsed e ON e.GBC = p.GBC
) as r
) as b
) as u
left join #tblParts as p1 on u.GBC = p1.GBC and ISNULL(u.Quantity,0) = ISNULL(p1.Quantity,0)
left join #tblPartsUsed as p2 on u.GBC = p2.GBC and ISNULL(u.Buildneed,0) = ISNULL(p2.Buildneed,0)
where (u.Afterbuild is not null or u.Total is not null or u.Quantity - u.Buildneed < 0)
order by u.GBC, u.Quantity
As per #RomaRuzich this question needs expected results in a table format.
Also the Parts and Excel table structure is needed with some data to clarify the question.
Made some assumptions and created script with output results.
declare #tblParts table(
GBC int,
Barcode varchar(20),
[Location] varchar(20),
Bookstand varchar(10) default '',
Quantity int
)
declare #tblPartsUsed table(
GBC int,
Replaced varchar(1) default '',
[Description] varchar(50),
Buildneed int
)
insert into #tblParts(GBC, Barcode, [Location], Quantity)
select 72223, '', '', 0
union all
select 86911, '201901200','JD-01/',500
union all
select 86911, '201901166','JD-01/ 14', 100
insert into #tblPartsUsed(GBC, [Description], Buildneed)
select '72223', 'RES_TF', 60
union all
select '86911', 'CAP_CER, 10nf,0603', 768
union all
select '86911', 'CAP_CER, 10nf,0603', 768
SELECT distinct e.GBC, e.Replaced, e.[Description], Barcode, [Location],
Bookstand, Quantity, Buildneed,
p.Quantity - e.Buildneed as Afterbuild,
x.TotalQuantity - Buildneed as Totals
FROM #tblParts p Right JOIN #tblPartsUsed e ON e.GBC = p.GBC
left join (select GBC, sum(Quantity) TotalQuantity from #tblParts group by GBC) x <br/>on e.GBC = x.GBC
ORDER BY e.GBC ASC, p.Quantity ASC
Related
How to order by result set in union query in T-SQL
I want to use order by clause in my last sql query and I have more than 3 union queries. I do not want to order the top 2 union query but I want to use order by clause in my last sql statement. Currently, getting error ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator. select 'Total Number of pat' Name, convert(varchar(20), count(id)) Number from table2 where id = 5 union select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number from table3 union select x.usertype, count(distinct userid) cnt from [dbo].table1 t cross apply (values ( case when t.userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end, t.userid )) x(usertype, userid) where t.date >= dateadd(day,-7, getdate()) group by x.usertype order by usertype desc
order by is sorting the result of the unions all together however you can introduce a orderIndex column for imlementing the right ordering. Here the sample: I've tried to build sample data in the following code. create table table1( userid varchar(100), usertype varchar(100), date date ) insert into table1(userid, date) values ('Einsmayr', '2020-10-27') insert into table1(userid, date) values ('Eins123', '2020-10-27') insert into table1(userid, date) values ('Einschmid', '2020-10-27') insert into table1(userid, date) values ('Einshuber', '2020-10-27') insert into table1(userid, date) values ('Einsreitmayr', '2020-10-27') create table table2 ( Name varchar(100), id int ) insert into table2(Name, id) values('Zweirich', 5) insert into table2(Name, id) values('Zweifel', 6) create table table3 ( Name varchar(100), id int ) insert into table3(Name, id) values('Dreisinger', 17) insert into table3(Name, id) values('Dreibert', 18) This allows the following queries: select usertype, Number from ( select 'Total Number of pat' usertype, convert(varchar(20), count(id)) Number, 1 orderIndex from table2 where id = 5 union select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number, 2 orderIndex from table3 union select usertype, count(distinct userid) Number, 3 orderIndex from ( select userid, case when userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end usertype from table1 where date >= dateadd(day,-7, getdate()) ) x group by x.usertype ) y order by y.orderIndex, y.usertype Find the solution here: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=ac396c48f5dbcb4a53ad40fac70e9236
SQL Server split overlapping date ranges
I need to split date ranges that overlap. I have a primary table (I've called it Employment for this example), and I need to return all Begin-End date ranges for a person from this table. I also have multiple sub tables (represented by Car and Food), and I want to return the value that was active in the sub tables during the times given in the main tables. This will involve splitting the main table date ranges when a sub table item changes. I don't want to return sub table information for dates not in the main tables. DECLARE #Employment TABLE ( Person_ID INT, Employment VARCHAR(50), Begin_Date DATE, End_Date DATE ) DECLARE #Car TABLE ( Person_ID INT, Car VARCHAR(50), Begin_Date DATE, End_Date DATE ) DECLARE #Food TABLE ( Person_ID INT, Food VARCHAR(50), Begin_Date DATE, End_Date DATE ) INSERT INTO #Employment ( [Person_ID], [Employment], [Begin_Date], [End_Date] ) VALUES ( 123 , 'ACME' , '1986-01-01' , '1990-12-31' ) , ( 123 , 'Office Corp' , '1995-05-15' , '1998-10-03' ) , ( 123 , 'Job 3' , '1998-10-04' , '2999-12-31' ) INSERT INTO #Car ( [Person_ID] , [Car] , [Begin_Date] , [End_Date] ) VALUES ( 123, 'Red Car', '1986-05-01', '1997-06-23' ) , ( 123, 'Blue Car', '1997-07-03', '2999-12-31' ) INSERT INTO #Food ( [Person_ID], [Food], [Begin_Date], [End_Date] ) VALUES ( 123, 'Eggs', '1997-01-01', '1997-03-09' ) , ( 123, 'Donuts', '2001-02-23', '2001-02-25' ) For the above data, the results should be: Person_ID Employment Food Car Begin_Date End_Date 123 ACME 1986-01-01 1986-04-30 123 ACME Red Car 1986-05-01 1990-12-31 123 Office Corp Red Car 1995-05-15 1996-12-31 123 Office Corp Eggs Red Car 1997-01-01 1997-03-09 123 Office Corp Red Car 1997-03-10 1997-06-23 123 Office Corp 1997-06-24 1997-07-02 123 Office Corp Blue Car 1997-07-03 1998-10-03 123 Job 3 Blue Car 1998-10-04 2001-02-22 123 Job 3 Donuts Blue Car 2001-02-23 2001-02-25 123 Job 3 Blue Car 2001-02-26 2999-12-31 The first row is his time working for ACME, where he didn't have a car or a weird food obsession. In the second row, he purchased a car, and still worked at ACME. In the third row, he changed jobs to Office Corp, but still has the Red Car. Note how we're not returning data during his unemployment gap, even though he had the Red Car. We only want to know what was in the Car and Food tables during the times there are values in the Employment table. I found a solution for SQL Server 2012 that uses the LEAD/LAG functions to accomplish this, but I'm stuck with 2008 R2.
To change the 2012 solution from that blog to work with 2008, you need to replace the LEAD in the following with ValidDates as … , ValidDateRanges1 as ( select EmployeeNo, Date as ValidFrom, lead(Date,1) over (partition by EmployeeNo order by Date) ValidTo from ValidDates ) There are a number of ways to do this, but one example is a self join to the same table + 1 row (which is effectively what a lead does). One way to do this is to put a rownumber on the previous table (so it is easy to find the next row) by adding another intermediate CTE (eg ValidDatesWithRowno). Then do a left outer join to that table where EmployeeNo is the same and rowno = rowno + 1, and use that value to replace the lead. If you wanted a lead 2, you would join to rowno + 2, etc. So the 2008 version would look something like with ValidDates as … , ValidDatesWithRowno as --This is the ValidDates + a RowNo for easy self joining below ( select EmployeeNo, Date, ROW_NUMBER() OVER (ORDER BY EmployeeNo, Date) as RowNo from ValidDates ) , ValidDateRanges1 as ( select VD.EmployeeNo, VD.Date as ValidFrom, VDLead1.Date as ValidTo from ValidDatesWithRowno VD left outer join ValidDatesWithRowno VDLead1 on VDLead1.EmployeeNo = VD.EmployeeNo and VDLead1.RowNo = VD.RowNo + 1 ) The rest of the solution described looks like it will work like you want on 2008.
Here is the answer I came up with. It works, but it's not very pretty. It goes it two waves, first splitting any overlapping Employment/Car dates, then running the same SQL a second time add the Food dates and split any overlaps again. DECLARE #Employment TABLE ( Person_ID INT, Employment VARCHAR(50), Begin_Date DATE, End_Date DATE ) DECLARE #Car TABLE ( Person_ID INT, Car VARCHAR(50), Begin_Date DATE, End_Date DATE ) DECLARE #Food TABLE ( Person_ID INT, Food VARCHAR(50), Begin_Date DATE, End_Date DATE ) INSERT INTO #Employment ( [Person_ID], [Employment], [Begin_Date], [End_Date] ) VALUES ( 123 , 'ACME' , '1986-01-01' , '1990-12-31' ) , ( 123 , 'Office Corp' , '1995-05-15' , '1998-10-03' ) , ( 123 , 'Job 3' , '1998-10-04' , '2999-12-31' ) INSERT INTO #Car ( [Person_ID] , [Car] , [Begin_Date] , [End_Date] ) VALUES ( 123, 'Red Car', '1986-05-01', '1997-06-23' ) , ( 123, 'Blue Car', '1997-07-03', '2999-12-31' ) INSERT INTO #Food ( [Person_ID], [Food], [Begin_Date], [End_Date] ) VALUES ( 123, 'Eggs', '1997-01-01', '1997-03-09' ) , ( 123, 'Donuts', '2001-02-23', '2001-02-25' ) DECLARE #Person_ID INT = 123; --A table to hold date ranges that need to be merged together DECLARE #DatesToMerge TABLE ( ID INT, Person_ID INT, Date_Type VARCHAR(10), Begin_Date DATETIME, End_Date DATETIME ) INSERT INTO #DatesToMerge SELECT ROW_NUMBER() OVER(ORDER BY [Car]) , Person_ID , 'Car' , Begin_Date , End_Date FROM #Car WHERE Person_ID = #Person_ID INSERT INTO #DatesToMerge SELECT ROW_NUMBER() OVER(ORDER BY [Employment]) , Person_ID , 'Employment' , Begin_Date , End_Date FROM #Employment WHERE Person_ID = #Person_ID; --A table to hold the merged #Employment and Car records DECLARE #EmploymentAndCar TABLE ( RowNumber INT, Person_ID INT, Begin_Date DATETIME, End_Date DATETIME ) ; WITH CarCTE AS (--This CTE grabs just the Car rows so we can compare and split dates from them SELECT ID, Person_ID, Date_Type, Begin_Date, End_Date FROM #DatesToMerge WHERE Date_Type = 'Car' ), NewRowsCTE AS ( --This CTE creates just new rows starting after the Car dates for each #Employment date range SELECT a.ID, a.Person_ID, a.Date_Type, DATEADD(DAY,1,b.End_Date) AS Begin_Date, a.End_Date FROM #DatesToMerge a INNER JOIN CarCTE b ON a.Begin_Date <= b.Begin_Date AND a.End_Date > b.Begin_Date AND a.End_Date > b.End_Date -- This is needed because if both the Car and #Employment end on the same date, there is split row after ), UnionCTE AS ( -- This CTE merges the new rows with the existing ones SELECT ID, Person_ID, Date_Type, Begin_Date, End_Date FROM #DatesToMerge UNION ALL SELECT ID, Person_ID, Date_Type, Begin_Date, End_Date FROM NewRowsCTE ), FixEndDateCTE AS ( SELECT CONVERT (CHAR,c.ID)+CONVERT (CHAR,c.Begin_Date) AS FixID, MIN(d.Begin_Date) AS Begin_Date FROM UnionCTE c LEFT OUTER JOIN CarCTE d ON c.Begin_Date < d.Begin_Date AND c.End_Date >= d.Begin_Date WHERE c.Date_Type <> 'Car' GROUP BY CONVERT (CHAR,c.ID)+CONVERT (CHAR,c.Begin_Date) ), Finalize AS ( SELECT ROW_NUMBER() OVER (ORDER BY e.Begin_Date) AS RowNumber, e.Person_ID, e.Begin_Date, CASE WHEN f.Begin_Date IS NULL THEN e.End_Date ELSE DATEADD (DAY,-1,f.Begin_Date) END AS EndDate FROM UnionCTE e LEFT OUTER JOIN FixEndDateCTE f ON (CONVERT (CHAR,e.ID)+CONVERT (CHAR,e.Begin_Date)) = f.FixID ) INSERT INTO #EmploymentAndCar ( RowNumber, Person_ID, Begin_Date, End_Date ) SELECT F.RowNumber , F.Person_ID , F.Begin_Date , F.EndDate FROM Finalize F INNER JOIN #Employment Employment ON F.Begin_Date BETWEEN Employment.Begin_Date AND Employment.End_Date AND Employment.Person_ID = #Person_ID ORDER BY F.Begin_Date -------------------------------------------------------------------------------------------------- --Now that the Employment and Car dates have been merged, empty the DatesToMerge table DELETE FROM #DatesToMerge; --Reload the DatesToMerge table with the newly-merged Employment and Car records, --and the Food records that still need to be merged INSERT INTO #DatesToMerge SELECT RowNumber , Person_ID , 'PtBCar' , Begin_Date , End_Date FROM #EmploymentAndCar WHERE Person_ID = #Person_ID INSERT INTO #DatesToMerge SELECT ROW_NUMBER() OVER(ORDER BY [Food]) , Person_ID , 'Food' , Begin_Date , End_Date FROM #Food WHERE Person_ID = #Person_ID ; WITH CarCTE AS (--This CTE grabs just the Food rows so we can compare and split dates from them SELECT ID, Person_ID, Date_Type, Begin_Date, End_Date FROM #DatesToMerge WHERE Date_Type = 'Food' ), NewRowsCTE AS ( --This CTE creates just new rows starting after the Food dates for each Employment date range SELECT a.ID, a.Person_ID, a.Date_Type, DATEADD(DAY,1,b.End_Date) AS Begin_Date, a.End_Date FROM #DatesToMerge a INNER JOIN CarCTE b ON a.Begin_Date <= b.Begin_Date AND a.End_Date > b.Begin_Date AND a.End_Date > b.End_Date -- This is needed because if both the Food and Car/Employment end on the same date, there is split row after ), UnionCTE AS ( -- This CTE merges the new rows with the existing ones SELECT ID, Person_ID, Date_Type, Begin_Date, End_Date FROM #DatesToMerge UNION ALL SELECT ID, Person_ID, Date_Type, Begin_Date, End_Date FROM NewRowsCTE ), FixEndDateCTE AS ( SELECT CONVERT (CHAR,c.ID)+CONVERT (CHAR,c.Begin_Date) AS FixID, MIN(d.Begin_Date) AS Begin_Date FROM UnionCTE c LEFT OUTER JOIN CarCTE d ON c.Begin_Date < d.Begin_Date AND c.End_Date >= d.Begin_Date WHERE c.Date_Type <> 'Food' GROUP BY CONVERT (CHAR,c.ID)+CONVERT (CHAR,c.Begin_Date) ), Finalize AS ( SELECT ROW_NUMBER() OVER (ORDER BY e.Begin_Date) AS RowNumber, e.Person_ID, e.Begin_Date, CASE WHEN f.Begin_Date IS NULL THEN e.End_Date ELSE DATEADD (DAY,-1,f.Begin_Date) END AS EndDate FROM UnionCTE e LEFT OUTER JOIN FixEndDateCTE f ON (CONVERT (CHAR,e.ID)+CONVERT (CHAR,e.Begin_Date)) = f.FixID ) SELECT DISTINCT F.Person_ID , Employment , Car , Food , F.Begin_Date , F.EndDate FROM Finalize F INNER JOIN #Employment Employment ON F.Begin_Date BETWEEN Employment.Begin_Date AND Employment.End_Date AND Employment.Person_ID = #Person_ID LEFT JOIN #Car Car ON Car.[Begin_Date] <= F.Begin_Date AND Car.[End_Date] >= F.[EndDate] AND Car.Person_ID = #Person_ID LEFT JOIN #Food Food ON Food.[Begin_Date] <= F.[Begin_Date] AND Food.[End_Date] >= F.[EndDate] AND Food.Person_ID = #Person_ID ORDER BY F.Begin_Date If anyone has a more elegant solution, I will be happy to accept their answer.
sql recursion: find tree given middle node
I need to get a tree of related nodes given a certain node, but not necessary top node. I've got a solution using two CTEs, since I am struggling to squeeze it all into one CTE :). Might somebody have a sleek solution to avoid using two CTEs? Here is some code that I was playing with: DECLARE #temp AS TABLE (ID INT, ParentID INT) INSERT INTO #temp SELECT 1 ID, NULL AS ParentID UNION ALL SELECT 2, 1 UNION ALL SELECT 3, 2 UNION ALL SELECT 4, 3 UNION ALL SELECT 5, 4 UNION ALL SELECT 6, NULL UNION ALL SELECT 7, 6 UNION ALL SELECT 8, 7 DECLARE #startNode INT = 4 ;WITH TheTree (ID,ParentID) AS ( SELECT ID, ParentID FROM #temp WHERE ID = #startNode UNION ALL SELECT t.id, t.ParentID FROM #temp t JOIN TheTree tr ON t.ParentID = tr.ID ) SELECT * FROM TheTree ;WITH Up(ID,ParentID) AS ( SELECT t.id, t.ParentID FROM #temp t WHERE t.ID = #startNode UNION ALL SELECT t.id, t.ParentID FROM #temp t JOIN Up c ON t.id = c.ParentID ) --SELECT * FROM Up ,TheTree (ID,ParentID) AS ( SELECT ID, ParentID FROM Up WHERE ParentID is null UNION ALL SELECT t.id, t.ParentID FROM #temp t JOIN TheTree tr ON t.ParentID = tr.ID ) SELECT * FROM TheTree thanks
Meh. This avoids using two CTEs, but the result is a brute force kludge that hardly qualifies as "sleek" as it won’t be efficient if your table is at all sizeable. It will: Recursively build all possible hierarchies As you build them, flag the target NodeId as you find it Return only the targeted tree I threw in column “TreeNumber” on the off-chance the TargetId appears in multiple hierarchies, or if you’d ever have multiple values to check in one pass. “Depth” was added to make the output a bit more legible. A more complex solution like #John’s might do, and more and subtler tricks could be done with more detailed table sturctures. DECLARE #startNode INT = 4 ;WITH cteAllTrees (TreeNumber, Depth, ID, ParentID, ContainsTarget) AS ( SELECT row_number() over (order by ID) TreeNumber ,1 ,ID ,ParentID ,case when ID = #startNode then 1 else 0 end ContainsTarget FROM #temp WHERE ParentId is null UNION ALL SELECT tr.TreeNumber ,tr.Depth + 1 ,t.id ,t.ParentID ,case when tr.ContainsTarget = 1 then 1 when t.ID = #startNode then 1 else 0 end ContainsTarget FROM #temp t INNER JOIN cteAllTrees tr ON t.ParentID = tr.ID ) SELECT TreeNumber ,Depth ,ID ,ParentId from cteAllTrees where TreeNumber in (select TreeNumber from cteAllTrees where ContainsTarget = 1) order by TreeNumber ,Depth ,ID
Here is a technique where you can select the entire hierarchy, a specific node with all its children, and even a filtered list and how they roll. Note: See the comments next to the DECLAREs Declare #YourTable table (id int,pt int,name varchar(50)) Insert into #YourTable values (1,null,'1'),(2,1,'2'),(3,1,'3'),(4,2,'4'),(5,2,'5'),(6,3,'6'),(7,null,'7'),(8,7,'8') Declare #Top int = null --<< Sets top of Hier Try 2 Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability Declare #Filter varchar(25) = '' --<< Empty for All or try 4,6 ;with cteP as ( Select Seq = cast(1000+Row_Number() over (Order by name) as varchar(500)) ,ID ,pt ,Lvl=1 ,name From #YourTable Where IsNull(#Top,-1) = case when #Top is null then isnull(pt,-1) else ID end Union All Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.name)) as varchar(500)) ,r.ID ,r.pt ,p.Lvl+1 ,r.name From #YourTable r Join cteP p on r.pt = p.ID) ,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP) ,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID ) Select Distinct A.R1 ,B.R2 ,A.ID ,A.pt ,A.Lvl ,name = Replicate(#Nest,A.Lvl-1) + A.name From cteR1 A Join cteR2 B on A.ID=B.ID Join (Select R1 From cteR1 where IIF(#Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',#Filter+','))>0) F on F.R1 between A.R1 and B.R2 Order By A.R1
Creating a Void Function in PostgreSQL
I am getting an error on this create function code in Postgresql. The error says it is happening around Line 2 at DELETE, but it happens at WITH if I remove that line so I think it is a problem with the format of my Creat Function create or replace function retention_data(shopId integer) returns void as $$ delete from retention where shop_id = shopId; WITH ret_grid_step1 as ( select * from ( SELECT order_id as order_name, cust_name as cust_name, email as email, date(order_date) as created_at, count(*) as num_items_in_order, sum(total_price) as sales , rank() over (partition BY order_id ORDER BY cust_name ASC) as rnk_shipping_name, rank() over (partition BY order_id ORDER BY email ASC) as rnk_email FROM orders WHERE shop_id = shopId and order_date is not null and order_date > now()::date - 365 and order_date < now()::date + 1 group by 1,2,3,4 ) x where rnk_shipping_name = 1 and rnk_email = 1 ) insert into retention(shop_id, cust_name, email, last_purchase_dt, total_sales, num_orders, days_since_last_order) select shopId as shop_id, coalesce(b.cust_name,'null') as cust_name, a.email, a.last_purchase_dt, total_sales, num_orders, current_date - last_purchase_dt as days_since_last_order from ( select email, max(created_at) as last_purchase_dt, count(*) as num_orders, sum(sales) as total_sales from ret_grid_step1 group by 1 ) as a left join ( select email, cust_name, rank() over (partition BY email ORDER BY created_at DESC) as rnk from ret_grid_step1 --where cust_name is not null group by 1,2,created_at ) as b on a.email = b.email where b.rnk = 1 and a.email <> ''; $$ language plpgsql;
getting distinct rows based on two column values
I am trying to get distinct rows from a temporary table and output them to an aspx page. I am trying to use the value of one column and get the last entry made into that column. I have been trying to use inner join and max(). However i have been unsuccessful. Here is the code i have been trying to do it with. Declare #TempTable table ( viewIcon nvarchar(10), tenderType nvarchar(20), diaryIcon int, customerName nvarchar(100), projectName nvarchar(100), diaryEntry nvarchar(max), diaryDate nvarchar(20), pid nvarchar(20) ) insert into #TempTable( viewIcon, tenderType, diaryIcon, customerName, projectName, diaryEntry , diaryDate , pid ) select p.viewicon, p.[Tender Type], 1 diaryicon, c.[Customer Name], co.[Last Project], d.Action, co.[Diary Date], p.PID From Projects2 p Inner Join (select distinct Pno, max(convert(date,[date of next call],103)) maxdate from ProjectDiary group by Pno ) td on p.PID = td.Pno Inner Join contacts3 co on co.[Customer Number] = p.[Customer Number] Inner Join Customers3 c on p.[Customer Number] = c.[Customer Number] Inner Join ProjectDiary d on td.Pno = d.Pno Where CONVERT(Date, co.[Diary Date], 103) BETWEEN GETDATE()-120 AND GETDATE()-60 DECLARE #contactsTable TABLE (pid nvarchar(200), diaryDate date) insert into #contactsTable (t.pid, t.diarydate) select distinct pid as pid, MAX(CONVERT(DATE, diaryDate, 103)) as diaryDate from # TempTable t group by pid DECLARE #tempContacts TABLE (pid nvarchar(200)) insert into #tempContacts(pid) select pid from #contactsTable DECLARE #tempDiaryDate TABLE (diaryDate date) insert into #tempDiaryDate(diaryDate) select distinct MAX(CONVERT(DATE, diaryDate, 103)) from #TempTable select t.* from #TempTable t inner join (select distinct customerName, M AX(CONVERT(DATE, diaryDate, 103)) AS diaryDate from #TempTable group by customerName) tt on t t.customerName=t.customerName where t.pid not in (select Pno from ProjectDiary where convert(date,[Date Of Next Call],103) > GETDATE()) and t.viewIcon <> '098' and t.viewIcon <> '163' and t.viewIcon <> '119' and t.pid in (select distinct pid from #tempContacts) and CONVERT(DATE, t.diaryDate, 103) in (select distinct CONVERT(DATE, diaryDate, 103) f rom #tempDiaryDate) order by CONVERT(DATE, tt.diaryDate, 103) I am trying to get all the distinct customerName's using the max date to determine which record it uses.
Use a subquery. Without going through your entire sql statement, the general idea is: Select [Stuff] From table t Where date = (Select Max(Date) from table where customer = t.customer)