Related
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
I'm trying to update the ended_at and active columns in the test_subscription table when the max period_end has not passed.
I'm using the below query but I doubt it's the most idiomatic way. Any suggestions on improvements are very much welcome.
Creating the tables:
CREATE TABLE test_subscription (
id INTEGER PRIMARY key,
started_at timestamp,
ended_at TIMESTAMP,
active boolean
);
CREATE TABLE test_invoice (
id INTEGER PRIMARY key,
subscription_id INTEGER,
period_start timestamp,
period_end timestamp
);
INSERT INTO test_subscription (id, started_at, ended_at, active)
values(1, '2017-01-01 00:00:00', NULL, TRUE);
INSERT INTO test_subscription (id, started_at, ended_at, active)
values(2, '2017-01-01 00:00:00', NULL, TRUE);
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(1, 1, '2017-01-01 00:00:00', '2017-12-01 00:00:00');
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(2, 1, '2017-12-02 00:00:00', '2019-12-01 00:00:00');
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(3, 2, '2017-01-01 00:00:00', '2017-12-01 00:00:00');
I'm updating using the below.
UPDATE test_subscription
SET ended_at = (CASE WHEN (SELECT
MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
) < now()
THEN (SELECT MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
)
ELSE NULL
end),
active = (CASE WHEN (SELECT MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
) < now()
THEN TRUE
ELSE FALSE
end);
Updates like that are usually faster if you first collect all the aggregates, then run the update using that intermediate result. Co-related sub-queries tend to be much slower.
update test_subscription s
set ended_at = case when t.latest_end < current_timestamp then t.latest_end end,
active = t.latest_end < current_timestamp
from (
select s.id,
max(i.period_end) as latest_end
from test_subscription s
join test_invoice i on s.id = i.subscription_id
group by s.id
) t
where t.id = s.id;
Online example: http://rextester.com/NMMF41667
You can put the MAX within CASE
UPDATE test_subscription s
SET ( ended_at, active ) = (SELECT MAX(CASE
WHEN period_end < NOW() THEN
period_end
END),
MAX(CASE
WHEN period_end < NOW() THEN 'TRUE'
ELSE 'FALSE'
END) :: BOOLEAN
FROM test_invoice i
WHERE s.id = i.subscription_id);
Demo
I got a problem regarding missing rows in a table that is giving me a headache.
As base data, I have the following table:
declare #table table
(
id1 int,
id2 int,
ch char(1) not null,
val int
)
insert into #table values (1112, 121, 'A', 12)
insert into #table values (1351, 121, 'A', 13)
insert into #table values (1411, 121, 'B', 81)
insert into #table values (1312, 7, 'C', 107)
insert into #table values (1401, 2, 'A', 107)
insert into #table values (1454, 2, 'D', 107)
insert into #table values (1257, 6, 'A', 1)
insert into #table values (1269, 6, 'B', 12)
insert into #table values (1335, 6, 'C', 12)
insert into #table values (1341, 6, 'D', 5)
insert into #table values (1380, 6, 'A', 3)
The output should be ordered by id2 and follow a fixed sequence of ch, which should repeat until next id2 begins.
Sequence:
'A'
'B'
'C'
'D'
If the sequence or the pattern is interrupted, it should fill the missing rows with null, so that i get this result table:
id1 id2 ch val
----------------------------
1112 121 'A' 12
NULL 121 'B' NULL
NULL 121 'C' NULL
NULL 121 'D' NULL
1351 121 'A' 13
1411 121 'B' 81
NULL 121 'C' NULL
NULL 121 'D' NULL
NULL 7 'A' NULL
NULL 7 'B' NULL
1312 7 'C' 107
NULL 7 'D' NULL
1401 2 'A' 107
NULL 2 'B' NULL
NULL 2 'C' NULL
1454 2 'D' 107
and so on...
What I'm looking for is a way to do this without iterations.
I hope someone can help!
Thanks in advance!
A solution might be this:
declare #table table ( id1 int, id2 int, ch char(1) not null, val int )
insert into #table values (1112, 121, 'A', 12)
,(1351, 121, 'A', 13),(1411, 121, 'B', 81),(1312, 7, 'C', 107),(1401, 2, 'A', 107)
,(1454, 2, 'D', 107),(1257, 6, 'A', 1),(1269, 6, 'B', 12),(1335, 6, 'C', 12)
,(1341, 6, 'D', 5),(1380, 6, 'A', 3)
;with foo as
(select
*
,row_number() over (partition by id2 order by id1) rwn
,ascii(isnull(lag(ch,1) over (partition by id2 order by id1),'A'))-ascii('A') prev
,count(*) over (partition by id2,ch) nr
,ascii(ch)-ascii('A') cur
from #table
)
,bar as
(
select
*,case when cur<=prev and rwn>1 then 4 else 0 end + cur-prev step
from foo
)
,foobar as
(
select *,sum(step) over (partition by id2 order by id1 rows unbounded preceding) rownum
from bar
)
,iterations as
(
select id2,max(nr) nr from foo
group by id2
)
,blanks as
(
select
id2,ch chnr,char(ch+ascii('A') )ch,ROW_NUMBER() over (partition by id2 order by c.nr,ch)-1 rownum,c.nr
from iterations a
inner join (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) c(nr)
on c.nr<=a.nr
cross join (values (0),(1),(2),(3)) b(ch)
)
select
b.id1,a.id2,a.ch,b.val
from blanks a
left join foobar b
on a.id2=b.id2 and a.rownum=b.rownum
order by a.id2,a.rownum
I first make the query "foo" which looks at the row number and gets the previous value for ch for each id2.
"bar" then finds how many missing values there are between the rows. For instance If the previous was an A and the current is a c then there are 2. If the previous was an A and the current is an A, then there are 4!
"foobar" then adds the steps, thus numbering the original rows, where they should be in the final output.
"iterations" counts the number of times the "ABCD" rows should appear.
"BLANKS" then is all the final rows, that is for each id2, it outputs all the "ABCD" rows that should be in the final output, and numbers them in rownum
Finally I left join "foobar" with "BLANKS" on id2 and rownum. Thus we get the correct number of rows, and the places where there are values in the original is output.
If you can manage to add an extra column in your table, that defines which [id2] are part from the same sequence you can try this:
declare #table table
(
id1 int,
id2 int,
ch char(1) not null,
val int,
category int -- extra column
)
insert into #table values (1112, 121, 'A', 12, 1)
insert into #table values (1351, 121, 'A', 13, 2)
insert into #table values (1411, 121, 'B', 81, 2)
insert into #table values (1312, 7, 'C', 107, 3)
insert into #table values (1401, 2, 'A', 107, 4)
insert into #table values (1454, 2, 'D', 107, 4)
insert into #table values (1257, 6, 'A', 1, 5)
insert into #table values (1269, 6, 'B', 12, 5)
insert into #table values (1335, 6, 'C', 12, 5)
insert into #table values (1341, 6, 'D', 5, 5)
insert into #table values (1380, 6, 'A', 3, 5)
DECLARE #sequence table (seq varchar(1))
INSERT INTO #sequence values ('A'), ('B'), ('C'), ('D')
SELECT b.id1, a.id2, a.seq, b.val, a.category
INTO #T1
FROM (
SELECT *
FROM #table
CROSS JOIN #sequence
) A
LEFT JOIN (
SELECT * FROM #table
) B
ON 1=1
AND a.id1 = b.id1
AND a.id2 = b.id2
AND a.seq = b.ch
AND a.val = b.val
;WITH rem_duplicates AS (
SELECT *, dup = ROW_NUMBER() OVER (PARTITION by id2, seq, category ORDER BY id1 DESC)
FROM #T1
) DELETE FROM rem_duplicates WHERE dup > 1
SELECT * FROM #T1 ORDER BY id2 DESC, category ASC, seq ASC
DROP TABLE #T1
I'm little confused by your output, try this:
Update
DECLARE #table TABLE
(
row INT IDENTITY(1, 1) ,
id1 INT ,
id2 INT ,
ch CHAR(1) NOT NULL ,
val INT
);
DECLARE #Sequence TABLE ( ch3 CHAR(1) NOT NULL );
INSERT INTO #Sequence
VALUES ( 'A' );
INSERT INTO #Sequence
VALUES ( 'B' );
INSERT INTO #Sequence
VALUES ( 'C' );
INSERT INTO #Sequence
VALUES ( 'D' );
INSERT INTO #table
VALUES ( 1112, 121, 'A', 12 );
INSERT INTO #table
VALUES ( 1351, 121, 'A', 13 );
INSERT INTO #table
VALUES ( 1411, 121, 'B', 81 );
INSERT INTO #table
VALUES ( 1312, 7, 'C', 107 );
INSERT INTO #table
VALUES ( 1401, 2, 'A', 107 );
INSERT INTO #table
VALUES ( 1454, 2, 'D', 107 );
INSERT INTO #table
VALUES ( 1257, 6, 'A', 1 );
INSERT INTO #table
VALUES ( 1269, 6, 'B', 12 );
INSERT INTO #table
VALUES ( 1335, 6, 'C', 12 );
INSERT INTO #table
VALUES ( 1341, 6, 'D', 5 );
INSERT INTO #table
VALUES ( 1380, 6, 'A', 3 );
SELECT r.id1 ,
fin.id2 ,
ch3 ,
r.val
FROM ( SELECT *
FROM ( SELECT CASE WHEN r.chd - l.chd = 1 THEN 0
ELSE 1
END [gap in sq] ,
l.*
FROM ( SELECT id2 ,
ASCII(ch) chd ,
ch ,
val ,
id1 ,
row
FROM #table
) AS l
LEFT JOIN ( SELECT id2 ,
ASCII(ch) chd ,
row
FROM #table
) AS r ON l.row = r.row - 1
) AS temp ,
#Sequence s
WHERE temp.[gap in sq] = 1
OR ( temp.[gap in sq] = 0
AND s.ch3 = temp.ch
)
) AS fin
LEFT JOIN #table r ON r.id2 = fin.id2
AND r.id1 = fin.id1
AND r.ch = fin.ch3
I've the following schema and the data:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[EntryExitLogs]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[EntryExitLogs](
[DeviceLogId] [int] NOT NULL,
[EmployeeCode] [nvarchar](50) NOT NULL,
[LogDate] [datetime] NOT NULL,
[Direction] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_EntryExitLogs] PRIMARY KEY CLUSTERED
(
[EmployeeCode] ASC,
[LogDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
GO
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('435859','30032','2014-01-21 07:04:41','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('438019','30032','2014-01-21 08:59:09','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441564','30032','2014-01-21 16:57:35','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441263','30032','2014-01-21 19:09:19','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441264','30032','2014-01-21 19:10:20','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('439928','34035','2014-01-21 08:29:59','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('437962','34035','2014-01-21 08:30:12','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('437992','34035','2014-01-21 08:47:33','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('440203','34035','2014-01-21 13:38:56','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('442858','34035','2014-01-21 16:34:08','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('442860','34035','2014-01-21 16:35:11','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441283','34035','2014-01-21 19:16:58','out');
I've written SQL to calculate In and out times like this:
;WITH cte AS (
SELECT ROW_NUMBER() OVER(
PARTITION BY lt.EmployeeCode ORDER BY lt.EmployeeCode,
lt.LogDate
) AS RowNo,
lt.DeviceLogId,
lt.EmployeeCode,
lt.LogDate,
lt.Direction
FROM EntryExitLogs lt
)
SELECT i.EmployeeCode,
i.LogDate AS InTime,
(
SELECT MIN(o.LogDate)
FROM cte AS o
WHERE o.EmployeeCode = i.EmployeeCode
AND o.RowNo = (i.RowNo + 1)
AND o.Direction = 'out'
) AS OutTime
FROM cte AS i
WHERE i.Direction = 'in'
ORDER BY i.EmployeeCode, i.LogDate
I'm getting the output (but not as desired by me), but I expect the output in the following manner (comments are given for more information against each row):
EmployeeCode InTime OutTime Comments
30032 21-Jan-2014 07:04:41 21-Jan-2014 08:59:09
30032 21-Jan-2014 16:57:35 21-Jan-2014 19:09:19
30032 21-Jan-2014 19:10:20 NULL If no OUT is specified for the last IN then it should be NULL
34035 21-Jan-2014 08:29:59 21-Jan-2014 13:38:56 Earliest IN and Latest OUT to be taken in case of multiple IN & OUT
34035 21-Jan-2014 16:34:08 21-Jan-2014 19:16:58 Earliest IN and Latest OUT to be taken in case of multiple IN & OUT
Please find the schema for this here
Kindly help me to achieve this.
This should work:
;WITH TrueOut AS --select only the latest "out" in between two "ins"
(
SELECT *
FROM EntryExitLogs a
WHERE direction='out'
AND
ISNULL((SELECT MIN(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='out'
AND b.LogDate>a.LogDate),'9999-12-31')
>=
ISNULL((SELECT MIN(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='in'
AND b.LogDate>a.LogDate),'9999-12-31')
),
TrueIn AS --select only the earlies "in" in between two "outs"
(
SELECT *
FROM EntryExitLogs a
WHERE direction='in'
AND
ISNULL((SELECT MAX(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='out'
AND b.LogDate<a.LogDate),'1900-01-01')
>=
ISNULL((SELECT MAX(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='in'
AND b.LogDate<a.LogDate),'1900-01-01')
)
-- For every in select the next out
SELECT a.EmployeeCode, a.LogDate InTime,
(SELECT MIN(LogDate)
FROM TrueOut b
WHERE a.EmployeeCode=b.EmployeeCode
AND a.LogDate<b.LogDate) OutTIme
FROM TrueIn a
This isn't your standard "how do I find duplicates" question, I know how to do find duplicates, see below. This question is how do I update said records that also have child items with matching records?
Alright, I'm going to give you whole scenario so that you can work with this problem.
Duplicate records could be inserted as a result of critical system failure.
Finding later duplicates and marking the parent commission_import_commission_junction "is_processed = True" solves this problem.
The complication is that the commission_import_commission_junction and its children commission_import_commission_junction_line_items must be identical on the columns to compare.
the tables are:
commission_import_commission_junction
- id
- created_date
- some columns that are checked for duplication
- some columns that are not checked for duplication
commission_import_commission_junction_line_items
- id
- some columns that are checked for duplication
- some columns that are not checked for duplication
(For the full table spec, check out the CREATE TABLE statements in the bottom-most block of code.)
The query to mark duplicates on just the parent table commission_import_commission_junction:
UPDATE commission_import_commission_junction cicj
SET is_processed = TRUE
FROM (
SELECT MIN(created_date) AS first_date, member_id, site_id, action_status, action_type, ad_id, commission_id, country, event_date, locking_date, order_id, original, original_action_id, posting_date, website_id, advertiser_name, commission_amount, sale_amount, aggregator_affiliate_id
FROM commission_import_commission_junction inner_imports
JOIN commission_import_commission_junction_line_items inner_items ON inner_items.commission_import_commission_junction_id = inner_imports.commission_import_commission_junction_id
GROUP BY member_id, site_id, action_status, action_type, ad_id, commission_id, country, event_date, locking_date, order_id, original, original_action_id, posting_date, website_id, advertiser_name, commission_amount, sale_amount, aggregator_affiliate_id
HAVING (COUNT(*) > 1)
) AS dups
WHERE
-- MAIN TABLE COLUMNN LIST
(cicj.member_id, cicj.site_id, cicj.action_status, cicj.action_type, cicj.ad_id, cicj.commission_id, cicj.country, cicj.event_date, cicj.locking_date, cicj.order_id, cicj.original, cicj.original_action_id, cicj.posting_date, cicj.website_id, cicj.advertiser_name, cicj.commission_amount, cicj.sale_amount, cicj.aggregator_affiliate_id)
IS NOT DISTINCT FROM
-- OTHER TABLE COLUMN LIST
(dups.member_id, dups.site_id, dups.action_status, dups.action_type, dups.ad_id, dups.commission_id, dups.country, dups.event_date, dups.locking_date, dups.order_id, dups.original, dups.original_action_id, dups.posting_date, dups.website_id, dups.advertiser_name, dups.commission_amount, dups.sale_amount, dups.aggregator_affiliate_id)
AND cicj.created_date <> dups.first_date
AND cicj.is_processed = FALSE;
Somewhere and somehow I need to check that the line_items are also duplicates.
THE CODE BELOW IS TO SETUP THE DATABASE, remember this is postgres specific.
-- "commission_import_build" is a record that keeps information about the process of collecting the commission information. Duplicate commission_import_commission_junction records will not exist with the same commission_import_build_id
-- "commission_import_commission_junction" is a record description commission information from a customers purchase.
-- "commission_import_commission_junction_line_items" are records describing items in that purchase.
DROP TABLE IF EXISTS commission_import_commission_junction_line_items;
DROP TABLE IF EXISTS commission_import_commission_junction;
DROP TABLE IF EXISTS commission_import_builds;
CREATE TABLE commission_import_builds
(
commission_import_build_id serial NOT NULL,
build_date timestamp with time zone NOT NULL,
CONSTRAINT pkey_commission_import_build_id PRIMARY KEY (commission_import_build_id),
CONSTRAINT commission_import_builds_build_date_key UNIQUE (build_date)
);
INSERT INTO commission_import_builds (commission_import_build_id, build_date) VALUES (1, '2011-01-01');
INSERT INTO commission_import_builds (commission_import_build_id, build_date) VALUES (2, '2011-01-02');
INSERT INTO commission_import_builds (commission_import_build_id, build_date) VALUES (3, '2011-01-03');
CREATE TABLE commission_import_commission_junction
(
commission_import_commission_junction_id serial NOT NULL,
member_id integer,
site_id integer,
action_status character varying NOT NULL,
action_type character varying NOT NULL,
ad_id bigint,
commission_id bigint NOT NULL,
country character varying,
event_date timestamp with time zone NOT NULL,
locking_date timestamp with time zone,
order_id character varying NOT NULL,
original boolean,
original_action_id bigint NOT NULL,
posting_date timestamp with time zone NOT NULL,
website_id bigint NOT NULL,
advertiser_name character varying,
commission_amount numeric(19,2) NOT NULL,
sale_amount numeric(19,2) NOT NULL,
aggregator_affiliate_id integer NOT NULL,
is_processed boolean NOT NULL DEFAULT false,
created_date timestamp with time zone NOT NULL DEFAULT now(),
member_transaction_id integer,
commission_import_build_id integer NOT NULL,
CONSTRAINT pkey_commission_import_commission_junction_commission_import_co PRIMARY KEY (commission_import_commission_junction_id),
CONSTRAINT fk_commission_import_commission_junction_commission_import_buil FOREIGN KEY (commission_import_build_id)
REFERENCES commission_import_builds (commission_import_build_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE INDEX idx_commission_import_commission_junction_is_processed
ON commission_import_commission_junction
USING btree
(is_processed);
INSERT INTO commission_import_commission_junction (commission_import_commission_junction_id, action_status, action_type, commission_id, event_date, order_id, original_action_id, posting_date, website_id, commission_amount, sale_amount, aggregator_affiliate_id, commission_import_build_id, created_date) VALUES
(1, 'new', 'sale', 1234, '2011-02-04 14:39:52.989499-07', 'test-order', 1234567, '2011-02-04 14:39:52.989499-07', 123, 12.35, 123.45, 9876, 1, '2011-02-05');
INSERT INTO commission_import_commission_junction (commission_import_commission_junction_id, action_status, action_type, commission_id, event_date, order_id, original_action_id, posting_date, website_id, commission_amount, sale_amount, aggregator_affiliate_id, commission_import_build_id, created_date) VALUES
(2, 'new', 'sale', 1234, '2011-02-04 14:39:52.989499-07', 'test-order', 1234567, '2011-02-04 14:39:52.989499-07', 123, 12.35, 123.45, 9876, 2, '2011-02-06');
INSERT INTO commission_import_commission_junction (commission_import_commission_junction_id, action_status, action_type, commission_id, event_date, order_id, original_action_id, posting_date, website_id, commission_amount, sale_amount, aggregator_affiliate_id, commission_import_build_id, created_date) VALUES
(3, 'new', 'sale', 1234, '2011-02-04 14:39:52.989499-07', 'test-order', 1234567, '2011-02-04 14:39:52.989499-07', 123, 12.35, 123.45, 9876, 3, '2011-02-07');
SELECT * FROM commission_import_commission_junction;
CREATE TABLE commission_import_commission_junction_line_items
(
commission_import_commission_junction_line_item_id serial NOT NULL,
commission_import_commission_junction_id integer NOT NULL,
sku character varying,
quantity integer,
posting_date timestamp with time zone,
sale_amount numeric(19,2),
discount numeric(19,2),
CONSTRAINT pkey_commission_import_commission_junction_link_items_commissio PRIMARY KEY (commission_import_commission_junction_line_item_id),
CONSTRAINT fkey_commission_import_commission_junction_line_items_commissio FOREIGN KEY (commission_import_commission_junction_id)
REFERENCES commission_import_commission_junction (commission_import_commission_junction_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (1, 'test1', 3, 23.45);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (1, 'test2', 3, 67.50);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (1, 'test3', 3, 32.50);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (2, 'test1', 3, 23.45);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (2, 'test2', 3, 67.50);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (2, 'test3', 3, 32.50);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (3, 'test1', 3, 23.45);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (3, 'test2', 3, 67.50);
INSERT INTO commission_import_commission_junction_line_items (commission_import_commission_junction_id, sku, quantity, sale_amount) VALUES (3, 'test3', 3, 32.50);
Reminds me of duplicate elimination in direct marketing mailing lists
Regardless of the details of your tables, a parent-child dupe elimination algorithm follows these steps:
1) Get duplicates into a list that matches old key to new key (temp table)
2) Update the foreign key in the child table
3) Delete the dupes from the parent
I admire the detail in your post, but I'm going to keep it simple and easier to read with some example table/column names:
-- step 1, get the list
-- Warning: t-sql syntax, adjust for Postgres
-- if it doesn't like placement of "into..." clause
select keep.primaryKey as keepKey
, dupe.primaryKey as dupeKey
into #DupeList
from (
select min(primaryKey) as primaryKey
, dupeCriteria1
, dupeCriteria2
FROM theTable
group by dupeCriteria1,dupeCritera2
having count(*) > 1
) keep
JOIN theTable dupe
ON keep.dupeCriteria1 = dupe.dupeCriteria1
AND keep.dupeCriteria2 = dupe.dupeCriteria2
AND keep.primaryKey <> dupe.primaryKey
Once you have that, update the foreign key in the child table:
update childTable
set foreignKey = #temp1.keepKey
from #temp1
where foreignKey = #temp1.dupeKey
Then just delete everything out of the parent table:
delete from parentTable
where primaryKey in (select dupeKey from #temp1)
CREATE FUNCTION removeCommissionImportCommissionJunctionDuplicates() RETURNS INT AS $BODY$ DECLARE duplicate RECORD; DECLARE parent RECORD; DECLARE children commission_import_commission_junction_line_items[]; DECLARE duplicate_children commission_import_commission_junction_line_items[]; DECLARE duplicate_child_count INT; DECLARE child commission_import_commission_junction_line_items; DECLARE duplicate_child commission_import_commission_junction_line_items; DECLARE num_updates INT; BEGIN
SELECT * FROM (SELECT 0) AS value INTO num_updates;
FOR duplicate IN
SELECT cicj.*, dups.first_date
FROM commission_import_commission_junction cicj
JOIN (SELECT MIN(created_date) AS first_date, member_id, site_id, action_status, action_type, ad_id, commission_id, country, event_date, locking_date, order_id, original, original_action_id, posting_date, website_id, advertiser_name, commission_amount, sale_amount, aggregator_affiliate_id
FROM commission_import_commission_junction inner_imports
GROUP BY member_id, site_id, action_status, action_type, ad_id, commission_id, country, event_date, locking_date, order_id, original, original_action_id, posting_date, website_id, advertiser_name, commission_amount, sale_amount, aggregator_affiliate_id
HAVING (COUNT(*) > 1)) AS dups
ON (cicj.member_id, cicj.site_id, cicj.action_status, cicj.action_type, cicj.ad_id, cicj.commission_id, cicj.country, cicj.event_date, cicj.locking_date, cicj.order_id, cicj.original, cicj.original_action_id, cicj.posting_date, cicj.website_id, cicj.advertiser_name, cicj.commission_amount, cicj.sale_amount, cicj.aggregator_affiliate_id)
IS NOT DISTINCT FROM
(dups.member_id, dups.site_id, dups.action_status, dups.action_type, dups.ad_id, dups.commission_id, dups.country, dups.event_date, dups.locking_date, dups.order_id, dups.original, dups.original_action_id, dups.posting_date, dups.website_id, dups.advertiser_name, dups.commission_amount, dups.sale_amount, dups.aggregator_affiliate_id)
WHERE cicj.created_date != dups.first_date
AND cicj.is_processed = FALSE
LOOP
--RAISE NOTICE 'Looping';
-- We need to collect the parent and children of the original record.
-- Get the parent of the original
SELECT *
FROM commission_import_commission_junction cicj
WHERE (cicj.member_id, cicj.site_id, cicj.action_status, cicj.action_type, cicj.ad_id, cicj.commission_id, cicj.country, cicj.event_date, cicj.locking_date, cicj.order_id, cicj.original, cicj.original_action_id, cicj.posting_date, cicj.website_id, cicj.advertiser_name, cicj.commission_amount, cicj.sale_amount, cicj.aggregator_affiliate_id)
IS NOT DISTINCT FROM
(duplicate.member_id, duplicate.site_id, duplicate.action_status, duplicate.action_type, duplicate.ad_id, duplicate.commission_id, duplicate.country, duplicate.event_date, duplicate.locking_date, duplicate.order_id, duplicate.original, duplicate.original_action_id, duplicate.posting_date, duplicate.website_id, duplicate.advertiser_name, duplicate.commission_amount, duplicate.sale_amount, duplicate.aggregator_affiliate_id)
AND cicj.created_date = duplicate.first_date
INTO parent;
-- Get the children of the original
children := ARRAY(
SELECT cicjli
FROM commission_import_commission_junction_line_items cicjli
WHERE cicjli.commission_import_commission_junction_id
= parent.commission_import_commission_junction_id);
--RAISE NOTICE 'parent: %', parent;
--RAISE NOTICE 'children: %', children;
-- Now get the duplicates children
duplicate_children := ARRAY(
SELECT cicjli
FROM commission_import_commission_junction_line_items cicjli
WHERE cicjli.commission_import_commission_junction_id
= duplicate.commission_import_commission_junction_id);
--RAISE NOTICE 'duplicate_children: %', duplicate_children;
-- Next, compare the children of the duplicate to the children of the original parent.
-- First compare size
IF array_upper(children, 1) = array_upper(duplicate_children, 1) THEN
--RAISE NOTICE 'Same number of children in duplicate as in parent';
-- Now compare each set
SELECT * FROM (SELECT 0) AS value INTO duplicate_child_count;
FOR child_index IN array_lower(children, 1) .. array_upper(children, 1) LOOP
child := children[child_index];
FOR duplicate_child_index IN array_lower(duplicate_children, 1) .. array_upper(duplicate_children, 1) LOOP
duplicate_child := duplicate_children[duplicate_child_index];
IF (child.sku, child.quantity, child.posting_date, child.sale_amount, child.discount) IS NOT DISTINCT FROM (duplicate_child.sku, duplicate_child.quantity, duplicate_child.posting_date, duplicate_child.sale_amount, duplicate_child.discount) THEN
SELECT * FROM (SELECT duplicate_child_count + 1) AS value INTO duplicate_child_count;
EXIT;
END IF;
END LOOP;
END LOOP;
--RAISE NOTICE 'Duplicate Child Count: %', duplicate_child_count;
-- If we have the same number of duplicates as there are records
IF duplicate_child_count = array_upper(duplicate_children, 1) THEN
-- Update the duplicate record as processed.
--RAISE NOTICE 'Marking duplicate % as is_processed', duplicate;
UPDATE commission_import_commission_junction cicj SET is_processed = TRUE WHERE cicj.commission_import_commission_junction_id
= duplicate.commission_import_commission_junction_id;
SELECT * FROM (SELECT num_updates + 1) AS value INTO num_updates;
END IF;
END IF;
END LOOP;
--RAISE NOTICE 'Updates: %', num_updates;
RETURN num_updates; END; $BODY$ LANGUAGE plpgsql;