I know this question might sound like a duplicate, but I've been through every question I could find; though it's still possible it might be a duplicate of a question I might have missed.
I have what at surface value appears to be a trivial requirement but no matter how I script it out there's always some caveat that's just not working. I've tried GROUP, DISTINCT, JOIN, aggregate functions, etc.
Scenario:
PRIMARYTABLE contains a set of campaigns and SECONDARYTABLE contains the dates on which campaigns were run. There can be multiple runs per campaign and I've included a SUBKEY for each run.
Requirement:
I need to be able to get the most recently run campaigns into a list so the user can more easily select from the campaigns that get run the most frequent.
PRIMARYTABLE
KEYCOLUMN INFOCOLUMN
100000 Test 1
100001 Test Campaign
100002 Test Image 2
100003 Test Img
100004 Image Test
100005 Test
100006 Test Image 3
100007 Test Image 4
100008 Test Image 5
100009 Image Comparison Test 2
100010 Testing
100011 Test Fields
100012 Test 5
100013 test
SECONDARYTABLE
KEYCOLUMN SUBKEY DATECOLUMN
100000 100000 2017-06-02 04:09:57.593
100001 100001 2017-06-19 12:09:54.093
100001 100002 2017-06-27 10:51:14.140
100004 100003 2017-06-27 12:33:47.747
100006 100004 2017-06-28 10:29:53.387
100007 100005 2017-06-28 10:36:23.710
100008 100006 2017-06-29 22:31:03.790
100009 100007 2017-06-29 23:07:52.870
100009 100010 2017-10-04 16:05:40.583
100009 100011 2017-10-04 16:09:55.470
100011 100008 2017-09-08 14:02:28.017
100012 100009 2017-09-11 16:17:23.870
100013 100012 2017-11-07 16:55:55.403
100013 100013 2017-11-08 15:37:16.430
Below is somewhat of an idea of more or less what I'm after.
SELECT DISTINCT( a.[INFOCOLUMN] )
FROM [PRIMARYTABLE] a
INNER JOIN [SECONDARYTABLE] b ON ( a.[KEYCOLUMN] = b.[KEYCOLUMN] )
ORDER BY a.[DATECOLUMN]
Here's hoping for a Homer Simpson "Doh!" moment once I see how it's supposed to be done.
Much appreciated.
the most recently run campaigns >> use row_number() over(.. order by ... DESC)
that get run the most frequent >> use count(*) over(partition by ..)
Using window functions row_number() over() and count() over() enables selection by row of data that is "most recent" and ordering by "most frequent". Note that the DESCending order of dates brings about "recent" = 1.
select
p.*, s.*
from PRIMARYTABLE p
inner join (
select KEYCOLUMN, SUBKEY, DATECOLUMN
, row_number() over(partition by KEYCOLUMN order by DATECOLUMN DESC) recent
, count(*) over(partition by KEYCOLUMN) frequency
from SECONDARYTABLE
) s on p.KEYCOLUMN = s.KEYCOLUMN and s.recent = 1
order by s.frequency DESC, p.INFOCOLUMN
You can try this:
DECLARE #PRIMARYTABLE TABLE
(
[KEYCOLUMN] INT
,[INFOCOLUMN] VARCHAR(24)
);
DECLARE #SECONDARYTABLE TABLE
(
[KEYCOLUMN] INT
,[SUBKEY] INT
,[DATECOLUMN] DATETIME2
);
INSERT INTO #PRIMARYTABLE ([KEYCOLUMN], [INFOCOLUMN])
VALUES (100000, 'Test 1')
,(100001, 'Test Campaign')
,(100002, 'Test Image 2')
,(100003, 'Test Img')
,(100004, 'Image Test')
,(100005, 'Test')
,(100006, 'Test Image 3')
,(100007, 'Test Image 4')
,(100008, 'Test Image 5')
,(100009, 'Image Comparison Test 2')
,(100010, 'Testing')
,(100011, 'Test Fields')
,(100012, 'Test 5')
,(100013, 'test');
INSERT INTO #SECONDARYTABLE ([KEYCOLUMN], [SUBKEY], [DATECOLUMN])
VALUES (100000, 100000, '2017-06-02 04:09:57.593')
,(100001, 100001, '2017-06-19 12:09:54.093')
,(100001, 100002, '2017-06-27 10:51:14.140')
,(100004, 100003, '2017-06-27 12:33:47.747')
,(100006, 100004, '2017-06-28 10:29:53.387')
,(100007, 100005, '2017-06-28 10:36:23.710')
,(100008, 100006, '2017-06-29 22:31:03.790')
,(100009, 100007, '2017-06-29 23:07:52.870')
,(100009, 100010, '2017-10-04 16:05:40.583')
,(100009, 100011, '2017-10-04 16:09:55.470')
,(100011, 100008, '2017-09-08 14:02:28.017')
,(100012, 100009, '2017-09-11 16:17:23.870')
,(100013, 100012, '2017-11-07 16:55:55.403')
,(100013, 100013, '2017-11-08 15:37:16.430');
SELECT a.[INFOCOLUMN]
,b.[DATECOLUMN]
FROM #PRIMARYTABLE A
CROSS APPLY
(
SELECT TOP 1 [DATECOLUMN]
FROM #SECONDARYTABLE B
WHERE A.[KEYCOLUMN] = B.[KEYCOLUMN]
ORDER BY [DATECOLUMN] DESC
) b;
It will give you the last execution of each campaign. You can filter then by date or ORDER BY and get TOP N from the final query.
Or you can use ROW_NUMBER:
WITH DataSource AS
(
SELECT A.[INFOCOLUMN]
,B.[DATECOLUMN]
,ROW_NUMBER() OVER (PARTITION BY A.[KEYCOLUMN] ORDER BY B.[KEYCOLUMN]) AS [RowID]
FROM #PRIMARYTABLE A
INNER JOIN #SECONDARYTABLE B
ON A.[KEYCOLUMN] = B.[KEYCOLUMN]
)
SELECT [INFOCOLUMN]
,[DATECOLUMN]
FROM DataSource
WHERE [RowID] = 1;
try this, it will return the list of campaigns in most frequent order of use. Note campaigns never run wont appear in your list. in this case you will to do a left join
SELECT a.[INFOCOLUMN]
FROM [PRIMARYTABLE] a
/* left */ JOIN [SECONDARYTABLE] b ON a.[KEYCOLUMN] = b.[KEYCOLUMN]
group BY a.[infocolumn]
order by max(datecolumn) desc
here is a stub i did to test it
select 10000 id,'Campain A' cname into #a1 union all
select 10002,'Campain B' union all
select 10004,'Campain C' union all
select 10009,'Campain E'
select 10000 id,'20170101' thedate into #a2 union all
select 10000,'20170102' union all
select 10009,'20170103' union all
select 10002,'20170104' union all
select 10004,'20170105' union all
select 10000,'20170201' union all
select 10000,'20170302' union all
select 10009,'20170403' union all
select 10002,'20170104' union all
select 10004,'20170205' union all
select 10000,'20170101' union all
select 10004,'20170302' union all
select 10000,'20170103' union all
select 10002,'20170404' union all
select 10002,'20170105'
select #a1.cname
from #a1 join #a2 on #a1.id = #a2.id
group by #a1.cname
order by max(thedate) desc
Related
Hello I have this part of a view in an Oracle database and I must change it on Microsoft Sql Server.
with V_LOCHIERARHY_N
(nr, nivel, location, parent, systemid, siteid, orgid, count_a, count_wo, children)
AS
SELECT LEVEL, LPAD (' ', 2 * (LEVEL - 1)) || l.LOCATION nivel,
LOCATION, PARENT, systemid, siteid, orgid,
(SELECT COUNT (a.ancestor)
FROM locancestor a
WHERE a.LOCATION = l.LOCATION AND a.siteid = l.siteid),
NVL (COUNT (w.wonum), 0)
FROM maximo.workorder w
WHERE ( w.reportdate >
TO_TIMESTAMP ('2006-06-19 00:00:01',
'YYYY-MM-DD HH24:MI:SS.FF'
)
AND w.istask = 0
AND w.worktype <> 'P'
AND w.LOCATION = l.LOCATION
)
AND w.status <> 'CAN'),
l.children
FROM lochierarchy l
START WITH l.LOCATION = 'StartPoint'
CONNECT BY PRIOR l.LOCATION = l.PARENT AND l.siteid = 'SiteTest'
What I need from this script is to return all the children of a given entry (the description of the children which can be found in locations table).
I have a table with next columns:
Location Parent Systemid Children Siteid Origid Lochierarchyid
A001 StartPoint Primary 2 SiteTest X 106372
A002 A001 Primary 2 SiteTest X 105472
A003 A002 Primary 0 SiteTest X 98654
A004 A002 Primary 1 SiteTest X 875543
A004B A004 Primary 0 SiteTest X 443216
B005 StartPoint Primary 0 SiteTest X 544321
For example for given entry A001 will return
A002
A003
A004
A004B
B005
I have made this view below but I don't know how to integrate it with the first one. Also it doesn't return me the list in the corectly order
Parent
Children 1 of parent
Children a of children 1
children b of children 1
children 2 of parent
children a1 of children 2 and so on.
WITH testCTE AS
(
SELECT l.parent, l.location as child, l.location, l.lochierarchyid
FROM lochierarchy l
where location='SecondLocation' --and siteid='SiteTest'
UNION ALL
SELECT c.Parent, l.parent, l.location, l.lochierarchyid
FROM lochierarchy l
INNER JOIN testCTE c ON l.parent = c.location
)
SELECT *
FROM testCTE c
order BY c.parent,child asc
;
Can please someone help me? :)
Following the query proposed by mathguy, modified for MSSQL (2012)
with
inputs ( location, parent ) as (
select 'A001' , 'StartPoint' union all
select 'A002' , 'A001' union all
select 'A003' , 'A002' union all
select 'A004' , 'A002' union all
select 'A004B', 'A004' union all
select 'B005' , 'StartPoint'
),
r (lvl, location, ord ) as (
select 1, location, CAST(location AS VARCHAR(400))
from inputs
where parent = 'StartPoint'
union all
select r.lvl + 1, i.location, CAST(r.location + '/' + i.location AS VARCHAR(400))
from r join inputs i on r.location = i.parent
)
select REPLICATE(' ', 2 * (lvl-1)) + location as location
from r
order by ord
;
Ouput:
location
-------------------------------------------------------------------
A001
A002
A003
A004
A004B
B005
Here is how you can do this (in Oracle, the only flavor I know) using a recursive query. "The web" reports SQL Server implements recursive queries as well, and with the same syntax (I believe all of this is SQL Standard compliant, so that's not surprising). Give it a try.
Instead of creating a table, I put all the test data in the first CTE. When you try this solution, delete the CTE named inputs first, and use your actual table name in the rest of the query.
with
inputs ( location, parent ) as (
select 'A001' , 'Downstream' from dual union all
select 'A002' , 'A001' from dual union all
select 'A003' , 'A002' from dual union all
select 'A004' , 'A002' from dual union all
select 'A004B', 'A004' from dual union all
select 'B005' , 'Downstream' from dual
),
r ( lvl, location ) as (
select 1, location
from inputs
where parent = 'Downstream'
union all
select r.lvl + 1, i.location
from r join inputs i on r.location = i.parent
)
search depth first by lvl set ord
select lpad(' ', 2 * (lvl-1), ' ') || location as location
from r
order by ord
;
LOCATION
--------------------
A001
A002
A003
A004
A004B
B005
6 rows selected.
ADDED: It seems SQL Server doesn't have the search depth/breadth first clause for recursive CTE's (or perhaps the syntax is different). In any case, here is a primitive "manual" implementation of the same:
with ( ......... ),
r ( lvl, location, ord ) as (
select 1, location, location
from inputs
where parent = 'Downstream'
union all
select r.lvl + 1, i.location, r.location || '/' || i.location
from r join inputs i on r.location = i.parent
)
select lpad(' ', 2 * (lvl-1), ' ') || location as location
from r
order by ord
;
I have a table with the following information
ID,DateTime,EventType
1,6/5/2013 9:35:00,B
1,6/5/2013 9:35:24,A
2,6/5/2013 9:35:36,B
3,6/5/2013 9:36:11,D
2,6/5/2013 9:39:16,A
3,6/5/2013 9:40:48,B
4,7/5/2013 9:35:19,B
4,7/5/2013 9:35:33,A
5,7/5/2013 9:35:53,B
5,7/5/2013 9:36:06,D
6,7/5/2013 9:39:39,A
7,7/5/2013 9:40:28,B
8,8/5/2013 9:35:02,A
7,8/5/2013 9:35:08,A
8,8/5/2013 9:35:29,B
6,8/5/2013 9:36:39,B
I need to count how many times each day an event changed state as long as the time between states was less than 30 seconds over the time period.
Basically I am looking for the following result set
6/5/2013 | 1
7/5/2013 | 2
8/5/2013 | 1
I've tried several different types of queries, but nothing works. I am using SQL Server Reporting Services 2008.
declare #t table (ID int,[DateTime] datetime ,EventType varchar);
insert #t values
(1,'6/5/2013 9:35:00','B'),
(1,'6/5/2013 9:35:24','A'),
(2,'6/5/2013 9:35:36','B'),
(3,'6/5/2013 9:36:11','D'),
(2,'6/5/2013 9:39:16','A'),
(3,'6/5/2013 9:40:48','B'),
(4,'7/5/2013 9:35:19','B'),
(4,'7/5/2013 9:35:33','A'),
(5,'7/5/2013 9:35:53','B'),
(5,'7/5/2013 9:36:06','D'),
(6,'7/5/2013 9:39:39','A'),
(7,'7/5/2013 9:40:28','B'),
(8,'8/5/2013 9:35:02','A'),
(7,'8/5/2013 9:35:08','A'),
(8,'8/5/2013 9:35:29','B'),
(6,'8/5/2013 9:36:39','B');
--select * from #t order by ID, DateTime;
with cte as (
select *, cast([DateTime] as date) the_date, row_number() over (partition by ID order by DateTime) row_num
from #t
)
select c1.the_date, count(1)
from cte c1
join cte c2
on c2.ID = c1.ID
and c2.row_num = c1.row_num + 1
where datediff(S,c1.DateTime, c2.DateTime) < 30
group by c1.the_date
order by c1.the_date;
Try this:
select CONVERT(VARCHAR(10), a.DateTime, 103) [Date], count(a.ID) Count from Table a
inner join Table b on a.ID = b.ID
where DATEDIFF(second,a.DateTime,b.DateTime) between 1 and 29 and a.ID = b.ID
and Cast(a.DateTime as Date) = Cast(b.DateTime as date)
group by CONVERT(VARCHAR(10), a.DateTime, 103)
I have two tables one that maintain the assets and the other which maintains the sub assets:
Table 1: assethdr
assetid
0000000002
0000000003
and
Table 2: assetdet
assetsubid assetid
0000000001 0000000002
0000000002 0000000002
0000000003 0000000002
0000000001 0000000003
0000000001 0000000109
0000000002 0000000109
0000000003 0000000109
0000000004 0000000109
0000000005 0000000109
I did this query:
WITH cte_assets
as
(SELECT
CASE WHEN ROW_NUMBER() OVER(PARTITION BY h.assetid ORDER BY f.assetsubid) = 1
THEN 'BA-'+LTRIM(RTRIM(h.CpnyId))+'-'+right(LTRIM(h.AssetId),5)+'-'+'0001' ELSE '' END as [Business Asset Number]
, 'BA'+
+'-'+RIGHT(LTRIM(RTRIM(ltrim(rtrim(f.cpnyid)))),3)
+'-'+
RIGHT(f.assetid,5)+'-'+RIGHT(f.assetsubid,4)as [Component Asset ID*]
FROM pssfaassets f
INNER JOIN PSSFAAssetsHdr h
ON f.AssetId=h.assetid
AND h.AssetId LIKE '%0000000002'
--GROUP BY h.cpnyid,h.AssetId,f.AssetSubId
)
SELECT * FROM cte_assets
WHERE [Business Asset Number]<>[Component Asset ID*]
ORDER BY [Component Asset ID*],[Business Asset Number]
but I don't get the right result, which should be :
Rownum Business Asset Number rownum2 Component Asset ID*
1 BA-613-00002-0001 1 BA-613-00002-0002
1 2 BA-613-00002-0003
2 BA-607-00109-001 1 BA-607-00109-0002
2 2 BA-607-00109-0003
2 3 BA-607-00109-0004
2 4 BA-607-00109-0005
Lets make some test data: I added the cpnyid (guessing based on your output) for both the header and detail records.
DECLARE #assethdr TABLE
(
assetid varchar(20),
CpnyId varchar(20)
)
INSERT INTO #assethdr
( assetid, CpnyId )
VALUES
('0000000002', '613'),
('0000000003', '605'),
('0000000109', '607');
DECLARE #assetdet TABLE
(
assetsubid varchar(20),
assetid varchar(20),
CpnyId varchar(20)
);
INSERT INTO #assetdet
( assetsubid, assetid, CpnyId )
VALUES
('0000000001', '0000000002', '613'),
('0000000002', '0000000002', '613'),
('0000000003', '0000000002', '613'),
('0000000001', '0000000003', '605'),
('0000000001', '0000000109', '607'),
('0000000002', '0000000109', '607'),
('0000000003', '0000000109', '607'),
('0000000004', '0000000109', '607'),
('0000000005', '0000000109', '607');
The query is similar, but I added the row number to the output:
WITH cte_assets
as
(SELECT
ROW_NUMBER() OVER (PARTITION BY h.assetid ORDER BY f.assetsubid) AS RN,
CASE WHEN ROW_NUMBER() OVER(PARTITION BY h.assetid ORDER BY f.assetsubid) = 2
THEN 'BA-'+LTRIM(RTRIM(h.CpnyId))+'-'+right(LTRIM(h.AssetId),5)+'-'+'0001' ELSE '' END as [Business Asset Number]
, 'BA'+
+'-'+RIGHT(LTRIM(RTRIM(ltrim(rtrim(f.cpnyid)))),3)
+'-'+
RIGHT(f.assetid,5)+'-'+RIGHT(f.assetsubid,4)as [Component Asset ID*]
FROM #assetdet f
INNER JOIN #assethdr h
ON f.AssetId=h.assetid
--AND h.AssetId LIKE '%0000000002'
--GROUP BY h.cpnyid,h.AssetId,f.AssetSubId
)
Now with the addition of the row number, you are trying to bypass the first match it seems (from your expected output):
SELECT (RN - 1) AS RowNum, [Business Asset Number], [Component Asset ID*]
FROM cte_assets
WHERE [RN] != 1
ORDER BY [Component Asset ID*],[Business Asset Number]
Here is the output:
RowNum Business Asset Number Component Asset ID*
1 BA-607-00109-0001 BA-607-00109-0002
2 BA-607-00109-0003
3 BA-607-00109-0004
4 BA-607-00109-0005
1 BA-613-00002-0001 BA-613-00002-0002
2 BA-613-00002-0003
I have to calculate the sum of total count value for each distinct arrival_time (column in database table) corresponding to column system_name. My sql query is:
SELECT system_name, COUNT(distinct arrival_time) AS c
FROM i2alarmlog
WHERE Ack_status = 0
AND Direction='CAME'
AND system_name in('I2-tciu database',
'i2-vcs logging',
'Indus1 Vacuum',
'Indus2 TCIU',
'Indus2 Vacuum',
'Septum_SIP2',
'TL3 Vacuum')
GROUP BY system_name
UNION ALL
SELECT 'sum' system_name,
Count(distinct arrival_time)
FROM i2alarmlog
WHERE Ack_status=0
AND Direction='CAME'
AND system_name in( 'I2-tciu database'
,'i2-vcs logging',
'Indus1 Vacuum',
'Indus2 Vacuum',
'Septum_SIP2',
'TL3 Vacuum')
When I run this sql query then sum is shown as 1841 but actually its 1845.
i2-vcs logging 2
I2-tciu database 2
Indus1 Vacuum 19
Indus2 TCIU 120
Indus2 Vacuum 1691
Septum_SIP2 8
TL3 Vacuum 3
sum 1841
In your second query, the distinct part is applied to all data. That means, if you have the same arrival time for 2 different products, it will only be counted once, and not once for each product. That would explain the difference between your expected and actual results.
What you could do instead is this:
;with cte as
(SELECT system_name, COUNT(distinct arrival_time) AS c
FROM i2alarmlog
WHERE Ack_status = 0
AND Direction='CAME'
AND system_name in('I2-tciu database','i2-vcs logging','Indus1 Vacuum','Indus2 TCIU','Indus2 Vacuum','Septum_SIP2','TL3 Vacuum')
GROUP BY system_name )
select system_name, c
from cte
union
select 'sum', sum(c) as c
from cte
Demo
One solution is by using an inline view and calculate the total sum like so:
SELECT system_name, COUNT(distinct arrival_time) AS c
FROM i2alarmlog
WHERE Ack_status = 0
AND Direction='CAME'
AND system_name in('I2-tciu database','i2-vcs logging','Indus1 Vacuum','Indus2 TCIU','Indus2 Vacuum','Septum_SIP2','TL3 Vacuum')
GROUP BY system_name
UNION ALL
select 'TotalSum' as TotalSum, sum(s.c) as TotalValue
from
(
SELECT system_name, COUNT(distinct arrival_time) AS c
FROM i2alarmlog
WHERE Ack_status = 0
AND Direction='CAME'
AND system_name in('I2-tciu database','i2-vcs logging','Indus1 Vacuum','Indus2 TCIU','Indus2 Vacuum','Septum_SIP2','TL3 Vacuum')
GROUP BY system_name
) s
With this solution the maximum agregation level (2) was reached ( sum(count(value)) ).
I have a history table containing a snapshot of each time a record is changed. I'm trying to return a certain history row with the original captured date. I am currently using this at the moment:
select
s.Description,
h.CaptureDate OriginalCaptureDate
from
HistoryStock s
left join
( select
StockId,
CaptureDate
from
HistoryStock
where
HistoryStockId in ( select MIN(HistoryStockId) from HistoryStock group by StockId )
) h on s.StockId = h.StockId
where
s.HistoryStockId = #HistoryStockId
This works but with 1 Million records its on the slow side and I'm not sure how to optimize this query.
How can this query be optimized?
UPDATE:
WITH OriginalStock (StockId, HistoryStockId)
AS (
SELECT StockId, min(HistoryStockId)
from HistoryStock group by StockId
),
OriginalCaptureDate (StockId, OriginalCaptureDate)
As (
SELECT h.StockId, h.CaptureDate
from HistoryStock h join OriginalStock o on h.HistoryStockId = o.HistoryStockId
)
select
s.Description,
h.OriginalCaptureDate
from
HistoryStock s left join OriginalCaptureDate h on s.StockId = h.StockId
where
s.HistoryStockId = #HistoryStockId
I've update the code to use CTE but I'm not better off performance wise, only have small performance increase. Any ideas?
Just another note, I need to get to the first record in the history table for StockId and not the earliest Capture date.
I am not certain I understand entirely how the data works from your query but nesting queries like that is never good for performance in my opinion. You could try something along the lines of:
WITH MinCaptureDate (StockID, MinCaptureDate)
AS (
SELECT HS.StockID
,MIN(HS.CaptureDate) AS OriginalCaptureDate
FROM HistoryStock HS
GROUP BY
HS.Description
)
SELECT HS.Description
,MCD.OriginalCaptureDate
FROM HistoryStock HS
JOIN MinCaptureDate MCD
ON HS.StockID = MCD.StockID
WHERE HS.StockID = #StockID
I think i see what you are trying to achieve. You basically want the description of the specified history stock record, but you want the date associated with the first history record for the stock... so if your history table looks like this
StockId HistoryStockId CaptureDate Description
1 1 Apr 1 Desc 1
1 2 Apr 2 Desc 2
1 3 Apr 3 Desc 3
and you specify #HistoryStockId = 2, you want the following result
Description OriginalCaptureDate
Desc 2 Apr 1
I think the following query would give you a slightly better performance.
WITH OriginalStock (StockId, CaptureDate, RowNumber)
AS (
SELECT
StockId,
CaptureDate,
RowNumber = ROW_NUMBER() OVER (PARTITION BY StockId ORDER BY HistoryStockId ASC)
from HistoryStock
)
select
s.Description,
h.CaptureDate
from
HistoryStock s left join OriginalStock h on s.StockId = h.StockId and h.RowNumber = 1
where
s.HistoryStockId = #HistoryStockId