Related
Is it possible to UNPIVOT data like this?
The columns CGL, CPL, EO should become Coverage Type, the values for CGL, CPL, EO should go in column Premium, and values for CGLTria,CPLTria,EOTria should go in column Tria Premium
Also if values for CGL,CPL and EO is 0 then I don't need those columns.
I am able to perform simple UNPIVOT , but confused when need to bring more columns
SELECT TOP 3
QuoteGUID, CoverageType, Premium
FROM
Align_EnvionmentalRating_PremiumHistory
UNPIVOT
(Premium FOR CoverageType IN (CGL, CPL, EO)) AS up
UPDATE: Adding more data
Columns: Policy Number, Policy Effective Date, Annual Statement Line, Fees, Risk State should stay the same.
Columns: CGL, CGLTria,CPL,CPLTria,EO,EOTria should be UNPIVOTED
select top 3 [Policy Number],
[Policy Effective Date],
'17.1' as [Annual Statement Line],
CGL,
CGLTria,
CPL,
CPLTria,
EO,
EOTria,
Fees,
[Risk State]
from #Test
UPDATE:
Adding consumable data:
create table dbo.TestDate (
PolicyNumber varchar(50),
PolicyEffectiveDate datetime,
AnnualStatementLine decimal(5,1),
CGL money,
CGLTria money,
CPL money,
CPLTria money,
EO money,
EOTria money,
Fees money,
RiskState varchar(2)
)
INSERT INTO dbo.TestDate (PolicyNumber, PolicyEffectiveDate , AnnualStatementLine, CGL , CGLTria , CPL ,CPLTria ,EO ,EOTria ,Fees ,RiskState )
values ('ENV560000001-00','2018-01-11 23:21:00',17.1,2000,160,674,54,341,0,250,'TX'),
('ENV560000002-00','2018-01-11 00:56:00',17.1,0,0,3238,259,0,0,250,'NV'),
('ENV560000003-00','2018-01-12 01:10:00',17.1,0,0,6045,484,0,0,250,'ND'),
('ENV560000004-00','2018-01-14 01:18:00',17.1,0,0,0,0,0,0,0,'ND')
select * from dbo.TestDate
Below query should work.
`Select * From
(select top 3 QuoteGUID, CoverageType, Premium
from Align_EnvionmentalRating_PremiumHistory
unpivot
(
Premium for CoverageType in ( CGL,CPL,EO)
) as up
) A
INNER JOIN
(select top 3 QuoteGUID, CoverageType, Premium
from Align_EnvionmentalRating_PremiumHistory
unpivot
(
TriaPremium for CoverageType in ( CGLTria,CPLTria,EOTria)
) as up
) as B
ON A.QuoteGUID=B.QuoteGUID AND A.CoverageType=substring(B.CoverageType,1,Len(B.CoverageType)-4)`
I'm not sure if this answers your question, but you could do this with simple UNION queries like this:
SELECT
ID = a1.ID,
CoverageType = 'CGL',
Premium = a1.CGL,
TriaPremium = a1.CGLTria
FROM
Align_EnvionmentalRating_PremiumHistory AS a1
WHERE
a1.CGL <> 0
UNION ALL
SELECT
ID = a2.ID,
CoverageType = 'CPL',
Premium = a2.CPL,
TriaPremium = a2.CPLTria
FROM
Align_EnvionmentalRating_PremiumHistory AS a2
WHERE
a2.CPL <> 0
UNION ALL
SELECT
ID = a3.ID,
CoverageType = 'EO',
Premium = a3.EO,
TriaPremium = a3.EOTria
FROM
Align_EnvionmentalRating_PremiumHistory AS a3
WHERE
a3.EO <> 0
This produces the exact output you state you want from the sample data provided. It could get kind of nasty with as many as 60 columns you have to normalize but it should be a one time thing to write it. Having worked through this a bit it seems you really need to separate this into at least two tables but is a whole can of worms.
with NormalizedData
(
PolicyNumber
, CoverageType
, Premium
, TriaPremium
) as
(
SELECT PolicyNumber
, 'CGL'
, CGL
, CGLTria
FROM TestDate
UNION ALL
SELECT PolicyNumber
, 'CPL'
, CPL
, CPLTria
FROM TestDate
UNION ALL
SELECT PolicyNumber
, 'EO'
, EO
, EOTria
FROM TestDate
)
select td.PolicyNumber
, td.PolicyEffectiveDate
, td.AnnualStatementLine
, nd.CoverageType
, nd.Premium
, nd.TriaPremium
, td.RiskState
from TestDate td
join NormalizedData nd on nd.PolicyNumber = td.PolicyNumber
order by td.PolicyNumber
, nd.CoverageType
I used CROSS APPLY operator to UNPIVOT the data, then included UNPIVOT statement in the INNER JOIN. That gave me desirable outcome.
declare #TestDate table (
QuoteGUID varchar(8000),
CGL money,
CGLTria money,
CPL money,
CPLTria money,
EO money,
EOTria money
)
INSERT INTO #TestDate (QuoteGUID, CGL , CGLTria , CPL ,CPLTria ,EO ,EOTria )
values ('2D62B895-92B7-4A76-86AF-00138C5C8540',2000,160,674,54,341,0),
('BE7F9483-174F-4238-8931-00D09F99F398',0,0,3238,259,0,0),
('BECFB9D8-D668-4C06-9971-0108A15E1EC2',0,0,0,0,0,0)
select A.QuoteGUID
,B.*
From #TestDate A
Cross Apply ( values ('CGL',CGL,CGLTria)
,('CPL',CPL,CPLTria)
,('CPL',EO,EOTria)
) B (CoverageType,Premium,TiraPremium)
I am looking to find a solution to this problem. I have a table called LogEntry that stores information used by multiple offices, where they have to log any visitors that come in to their office on any given day. If no visitors come in, they are still required to log "No Visitors" for the day. How do I run a query that pulls all dates where an office failed to create even a "No Visitors" log?
I've looked at this question (and the article linked within), but even adapting that query, I'm only able to create a blank row for a date where an office is missing an entry for a date, not specify the actual office that did not create an entry. Is there a way to do what I'm trying to do?
declare #temp table (
CDate datetime,
loc_id varchar(50)
)
insert into #temp SELECT DISTINCT entryDate, locationID FROM LogEntry WHERE entryDate >= '05/01/2017' AND entryDate <= '07-31-2017'
;with d(date) as (
select cast('05/01/2017' as datetime)
union all
select date+1
from d
where date < '07/31/2017'
)
select DISTINCT t.loc_id, CONVERT(date, d.date)
FROM d LEFT OUTER JOIN #temp t ON d.date = t.CDate
GROUP BY t.loc_id, d.date
ORDER BY t.loc_id
As I said, this query returns me a list of dates in the date range, and all locations that submitted entries on that date, but I'd like to find a way to extract essentially the opposite information: if an office (specified by locationID) did not submit an entry on a given day, return only those locationIDs and the dates that they missed.
Sample data
EntryID | locationID | entryDate
=================================
1 1 07-01-2017
2 1 07-02-2017
3 2 07-02-2017
4 1 07-04-2017
Expected Result (for date range of 07-01 to 07-04)
locationID | missedEntryDate
============================
1 07-03-2017
2 07-01-2017
2 07-03-2017
2 07-04-2017
Your first step was good, you create a list of all dates, but you also need a list of all locations. Then you create a cross join to have all combinations and then you perform the left join to find out what is missing.
;with allDates(date) as (
select cast('05/01/2017' as datetime)
union all
select date+1
from d
where date < '07/31/2017'
), allLocations as (
SELECT DISTINCT loc_id
FROM #temp
), allCombinations as (
SELECT date, loc_id
FROM allDates
CROSS JOIN allLocations
)
SELECT AC.loc_id, AC.date
FROM allCombinations AC
LEFT JOIN #temp t
ON AC.date = t.CDate
AND AC.loc_id = t.loc_id
WHERE t.loc_id IS NULL -- didnt find a match on #temp
If your dataset is not too large you can try this:
select t.loc_id, CONVERT(date, d.date)
FROM d
-- Cross join dates to all available locs
CROSS JOIN (SELECT DISTINCT loc_id FROM #temp ) AS Locs
LEFT JOIN
( SELECT loc_id, t.CDate
FROM #temp
GROUP BY loc_id, d.date ) AS t ON d.date = t.CDate AND Locs.loc_id = t.loc_id
ORDER BY Locs.loc_id
This should be a bit faster:
;WITH cte AS (
SELECT a.LocID, RangeStart.CDate, ( CASE WHEN Input.LocID IS NULL THEN 1 ELSE 0 END ) AS IsMissing
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
CROSS JOIN ( SELECT CONVERT( DATETIME, '2017-05-01' ) AS CDate ) AS RangeStart
LEFT JOIN
( SELECT LocID, MIN( CDate ) AS CDate
FROM #temp
WHERE CDate = '2017-05-01'
GROUP BY LocID ) AS Input ON a.LocID = Input.LocID AND RangeStart.CDate = Input.CDate
UNION ALL
SELECT a.LocID, a.CDate + 1 AS CDate,
ISNULL( ItExists, 0 ) AS IsMissing
FROM cte AS a
OUTER APPLY( SELECT LocID, 1 AS ItExists FROM #temp AS b WHERE a.LocID = b.LocID AND a.CDate + 1 = b.CDate ) AS c
WHERE a.CDate < '2017-07-01'
)
SELECT * FROM cte OPTION( MAXRECURSION 0 )
You can also add an index:
CREATE INDEX IX_tmp_LocID_CDate ON #temp( LocID, CDate )
Sample data set for the second query:
CREATE TABLE #temp( LocID VARCHAR( 50 ), CDate DATETIME )
INSERT INTO #temp
VALUES
( '1', '2017-05-01' ), ( '1', '2017-05-02' ), ( '1', '2017-05-03' ), ( '1', '2017-05-04' ), ( '1', '2017-05-05' ),
( '2', '2017-05-01' ), ( '2', '2017-05-02' ), ( '2', '2017-05-03' ), ( '2', '2017-05-04' ), ( '2', '2017-05-05' )
;WITH d AS (
SELECT CAST( '05/01/2017' AS DATETIME ) AS date
UNION ALL
SELECT date + 2
FROM d
WHERE date < '2018-07-31'
)
INSERT INTO #temp
SELECT LocID, d.date
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
CROSS JOIN d
OPTION( MAXRECURSION 0 )
I have Time table with column AmountDayOfMonth where values is NULL.
I need to update this column and insert count of days for every month.
Here is my update only for February:
update [Mart].[dbo].[Time]
set AmountDayOfMonth = (
select (count(theMonth)) as kol1
from [Mart].[dbo].[Time] as t1
where t1.NameMonth = 'February' group by t1.TheYear
)
You didn't specify the question, but I guess you have some calendar table with a column representing the total count of days in month. You will need only the second part. Just change table and column names to appropriate values.
Data initialization:
DECLARE #Time TABLE
(
TheYear INT ,
MonthNumber VARCHAR(MAX) ,
DayNumber INT ,
AmountDayOfMonth INT
);
WITH cte
AS ( SELECT CAST('20160101' AS DATE) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM cte
WHERE DATEADD(dd, 1, dt) < '20170101'
)
INSERT INTO #Time
( TheYear ,
MonthNumber ,
DayNumber
)
SELECT YEAR(dt) ,
MONTH(dt) ,
DAY(dt)
FROM cte
OPTION ( MAXRECURSION 0 );
Update statement you need:
WITH upd
AS ( SELECT * ,
COUNT(*) OVER ( PARTITION BY TheYear, MonthNumber ) AS cnt
FROM #Time
)
UPDATE upd
SET AmountDayOfMonth = cnt
SELECT *
FROM #Time
I'm tasked with the following:
Select a list of all customers who had their nth order during a certain date range (usually a specific month).
This list needs to contain: customer id, sum of first n orders
My tables are something like this:
[dbo.customers]: customerID
[dbo.orders]: orderID, customerID,
orderDate, orderTotal
Here is what I've tried so far:
-- Let's assume our threshold (n) is 10
-- Let's assume our date range is April 2013
-- Get customers that already had n orders before the beginning of the given date range.
DECLARE #tmpcustomers TABLE (tmpcustomerID varchar(8))
INSERT INTO
#tmpcustomers
SELECT
c.customerID
FROM
orders o
INNER JOIN customers c ON o.customerID = c.customerID
WHERE
o.orderDate < '2013-04-01'
GROUP BY c.customerID
HAVING (COUNT(o.orderID) >= 10)
-- Now get all customers that have n orders sometime within the given date range
-- but did not have n orders before the beginning of the given date range.
SELECT
a.customerID, SUM(orderTotal) AS firstTenOrderTotal
SELECT
o.customerID, o.orderID, o.orderTotal
FROM
orders o
INNER JOIN customers c ON c.customerID = o.customerID
WHERE
a.customerID NOT IN ( SELECT tmpcustomerID FROM #tmpcustomers )
AND
o.orderDate > '2013-04-01'
AND
o.orderDate < '2013-05-01'
GROUP BY c.customerID
HAVING COUNT(o.orderID) >= 10
This seems to work but it's clunky and slow. Another big problem is that the firstTenOrderTotal is actually the SUM of the total amount of orders by the end of the given date range and not necessarily the first 10.
Any suggestions for a better approach would be much appreciated.
In the insert to #tmpcustomers, why are you joining back to the customer table? The order table already has the customerID that you want. Also, why are you looking for orders where the order date is before your date range? Don't you just want customers with more than n orders between a date range? This will make the second query easier.
By only having the customers with n or more orders in the table variable #tmpcustomers, you should just be able to join it and the orders table in the second query to get the sum of all the orders for those customers where you would once again limit order table records to your date range (so you do not get orders outside of that range). This will remove the having statement and the join to the customers table in your final result query.
Give this a try. Depending on your order distribution it may perform better. In this query im assembling the list of orders in the range, and then looking back to count the number of prior orders (also grabbing the orderTotal).
note: I am assuming the orderID increments as orders are placed.
If this isnt the case just use a row_number over the date to project the sequence into the query.
declare #orders table (orderID int primary key identity(1,1), customerID int, orderDate datetime, orderTotal int)
insert into #orders (customerID, orderDate, orderTotal)
select 1, '2013-01-01', 1 union all
select 1, '2013-01-02', 2 union all
select 1, '2013-02-01', 3 union all
select 2, '2013-01-25', 5 union all
select 2, '2013-01-26', 5 union all
select 2, '2013-02-02', 10 union all
select 2, '2013-02-02', 10 union all
select 2, '2013-02-04', 20
declare #N int, #StartDate datetime, #EndDate datetime
select #N = 3,
#StartDate = '2013-02-01',
#EndDate = '2013-02-20'
select o.customerID,
[total] = o.orderTotal + p.total --the nth order + total prior
from #orders o
cross
apply ( select count(*)+1, sum(orderTotal)
from #orders
where customerId = o.customerID and
orderID < o.orderID and
orderDate <= o.orderDate
) p(n, total)
where orderDate between #StartDate and #EndDate and p.n = #N
Here is my suggestion:
Use Northwind
GO
select ords.OrderID , ords.OrderDate , '<-->' as Sep1 , derived1.* from
dbo.Orders ords
join
(
select CustomerID, OrderID, ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY OrderId DESC) AS ThisCustomerCardinalOrderNumber from dbo.Orders
) as derived1
on ords.OrderID = derived1.OrderID
where
derived1.ThisCustomerCardinalOrderNumber = 3
and ords.OrderDate between '06/01/1997' and '07/01/1997'
EDIT:::::::::
I took my CTE example, and reworked it for multiple Customers (seen below).
Give it the college try.
Use Northwind
GO
declare #BeginDate datetime
declare #EndDate datetime
select #BeginDate = '01/01/1900'
select #EndDate = '12/31/2010'
;
WITH
MyCTE /* http://technet.microsoft.com/en-us/library/ms175972.aspx */
( ShipName,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address],
City,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName,
ProductID,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight,ROWID) AS
(
SELECT
ShipName ,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address]
,City ,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName
,ProductID ,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight
, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate , ProductName ASC ) as ROWID /* Note that the ORDER BY (here) is directly related to the ORDER BY (near the very end of the query) */
FROM
dbo.Invoices inv /* “Invoices” is a VIEW, FYI */
where
(inv.OrderDate between #BeginDate and #EndDate)
)
SELECT
/*
ShipName,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address],
City,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName,
ProductID,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight,
*/
/*trim the list down a little for the final output */
CustomerID ,OrderID , OrderDate, (ExtendedPrice + Freight) as ComputedTotal
/*The below line is the “trick”. I reference the above CTE, but only get data that is less than or equal to the row that I am on (outerAlias.ROWID)*/
, (Select SUM (ExtendedPrice + Freight) from MyCTE innerAlias where innerAlias.ROWID <= outerAlias.ROWID and innerAlias.CustomerID = outerAlias.CustomerID) as RunningTotal
, ROWID as ROWID_SHOWN_FOR_KICKS , OrderDate as OrderDate
FROM
MyCTE outerAlias
GROUP BY CustomerID ,OrderID, OrderDate, ProductName,(ExtendedPrice + Freight) ,ROWID,OrderDate
/*Two Order By Options*/
ORDER BY outerAlias.CustomerID , outerAlias.OrderDate , ProductName
/* << Whatever the ORDER BY is here, should match the “ROW_NUMBER() OVER ( ORDER BY ________ ASC )” statement inside the CTE */
/*ORDER BY outerAlias.ROWID */ /* << Or, to keep is more “trim”, ORDER BY the ROWID, which will of course be the same as the “ROW_NUMBER() OVER ( ORDER BY” inside the CTE */
I need to concatenate rows with a date and a code into a date range
Table with two columns that are a composite primary key (date and a code )
Date Code
1/1/2011 A
1/2/2011 A
1/3/2011 A
1/1/2011 B
1/2/2011 B
2/1/2011 A
2/2/2011 A
2/27/2011 A
2/28/2011 A
3/1/2011 A
3/2/2011 A
3/3/2011 A
3/4/2011 A
Needs to be converted to
Start Date End Date Code
1/1/2011 1/3/2011 A
2/1/2011 2/2/2011 A
1/1/2011 1/2/2011 B
2/27/2011 3/4/2011 A
Is there any other way or is a cursor loop the only way?
declare #T table
(
[Date] date,
Code char(1)
)
insert into #T values
('1/1/2011','A'),
('1/2/2011','A'),
('1/3/2011','A'),
('1/1/2011','B'),
('1/2/2011','B'),
('3/1/2011','A'),
('3/2/2011','A'),
('3/3/2011','A'),
('3/4/2011','A')
;with C as
(
select *,
datediff(day, 0, [Date]) - row_number() over(partition by Code
order by [Date]) as rn
from #T
)
select min([Date]) as StartDate,
max([Date]) as EndDate,
Code
from C
group by Code, rn
sql server 2000 has it limitations. Rewrote the solution to make it more readable.
declare #t table
(
[Date] datetime,
Code char(1)
)
insert into #T values
('1/1/2011','A'),
('1/2/2011','A'),
('1/3/2011','A'),
('1/1/2011','B'),
('1/2/2011','B'),
('3/1/2011','A'),
('3/2/2011','A'),
('3/3/2011','A'),
('3/4/2011','A')
select a.code, a.date, min(b.date)
from
(
select *
from #t t
where not exists (select 1 from #t where t.code = code and t.date -1 = date)
) a
join
(
select *
from #t t
where not exists (select 1 from #t where t.code = code and t.date = date -1)
) b
on a.code = b.code and a.date <= b.date
group by a.code, a.date
Using a DatePart function for month will get you the "groups" you want
SELECT Min(Date) as StartDate, Max(Date) as EndDate, Code
FROM ThisTable Group By DatePart(m, Date), Code