best way to join query fields, union all/union - sql-server

I have 4 fields
cnpj 1
cnpj 2
cnpj 3
cnpj 4
all with the same typing, these fields go through an IN filter, for that I use UNION ALL in all 4 fields, is there a way to do this without union all ? or is it really the best way?
select id
from ( select id from db_armazenamento..arquivo_conhecimento
WITH(INDEX(IX_arquivo_conhecimento_6),nolock)
where dta_inclusao between getdate () -20 and getdate ()
and cpf_cnpj_emitente COLLATE database_default in (SELECT cnpj from
db_armazenamento..INTEGRA_ORCL_CTE)
union all
select id from db_armazenamento..arquivo_conhecimento
WITH(INDEX(IX_arquivo_conhecimento_6),nolock)
where dta_inclusao between getdate () -20 and getdate ()
and cpf_cnpj_destinatario COLLATE database_default in (SELECT cnpj from
db_armazenamento..INTEGRA_ORCL_CTE)
union all
select id from db_armazenamento..arquivo_conhecimento
WITH(INDEX(IX_arquivo_conhecimento_6),nolock)
where dta_inclusao between getdate () -20 and getdate ()
and cpf_cnpj_remetente COLLATE database_default in (SELECT cnpj from
db_armazenamento..INTEGRA_ORCL_CTE)
union all
select id from db_armazenamento..arquivo_conhecimento
WITH(INDEX(IX_arquivo_conhecimento_6),nolock)
where dta_inclusao between getdate () -20 and getdate ()
and cpf_cnpj_recebedor COLLATE database_default in (SELECT cnpj from
db_armazenamento..INTEGRA_ORCL_CTE)
) as c

To be honest I don't know why you keep selecting the rows over and over and over from the same table and then compare them to different columns be equal in another table. Pretty sure you could simplify the whole thing into something like this.
SELECT ac.id
FROM db_armazenamento.dbo.arquivo_conhecimento ac --you really should be explicit with the schema instead of relying on the default
join db_armazenamento.dbo.INTEGRA_ORCL_CTE ioc on ioc.cnpj in
(
ac.cpf_cnpj_emitente
, ac.cpf_cnpj_destinatario
, ac.cpf_cnpj_remetente
, ac.cpf_cnpj_recebedor
)
where ac.dta_inclusao >= dateadd(day, -20, getdate()
and ac.dta_inclusao <= getdate()

Related

How to UNPIVOT to normalize output SQL Server 2012

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)

Date compression SQL Server

I have this function and the query
Function:
ALTER FUNCTION [dbo].[dateholiday]
(#date1 AS DATETIME,
#date2 AS DATETIME )
RETURNS DATETIME
AS
BEGIN
DECLARE #Answer AS DATETIME
IF(#date1 = #date2)
SET #Answer = #date2
RETURN #Answer
END
the Query:
SELECT Posted_By
,Document_Number
,Account_Description
,dbo.dateholiday(CONVERT(NVARCHAR(10), CONVERT(DATETIME, Feiertage, 104), 20), [dbo].[QLIK].[Posted_Date]) AS Holiday
FROM [dbo].[FEIERTAGE_O]
,[dbo].[QLIK]
WHERE dbo.dateholiday(CONVERT(NVARCHAR(10), CONVERT(DATETIME, Feiertage, 104), 20), [dbo].[QLIK].[Posted_Date]) IS NOT NULL
GROUP BY dbo.dateholiday(CONVERT(NVARCHAR(10), CONVERT(DATETIME, Feiertage, 104), 20), [dbo].[QLIK].[Posted_Date])
,Posted_By
,Document_Number
,Account_Description
where FEIERTAGE_O is a list of 115 dates but the Posted_Date is about 1914495. The query works fine but the only problem is that it takes a lot and a lot of time. So I need to make it faster .
Any ideas !
Try if this helps:
SELECT Posted_By
, Document_Number
, Account_Description
, Holiday
FROM (
SELECT DISTINCT Posted_By
, Document_Number
, Account_Description
, dbo.dateholiday(CONVERT(DATETIME, Feiertage, 104)), [dbo].[QLIK].[Posted_Date]) AS Holiday
FROM [dbo].[FEIERTAGE_O]
CROSS JOIN [dbo].[QLIK]
) A
WHERE Holiday IS NOT NULL
Notice the cross join, you didn't specify an on clause for your implicit join (you should always use explicit join syntax), are you sure a cartesian product is what you need? This will explode the number of records (according to google calculator to infinity). Is that why you did a group by (even though you're not aggregating)? I've replaced the group by with a distinct, will give the same result, but is clearer. Have a look at your join whether it is really correct.
Edit
After re-reading your question I think this will give you what you need:
SELECT Posted_By
, Document_Number
, Account_Description
, Holiday
FROM (
SELECT DISTINCT Posted_By
, Document_Number
, Account_Description
, dbo.dateholiday(CONVERT(DATETIME, Feiertage, 104)), [dbo].[QLIK].[Posted_Date]) AS Holiday
FROM [dbo].[FEIERTAGE_O] FO
LEFT JOIN [dbo].[QLIK] Q
ON Q.[Posted_Date] = FO.Feiertage
) A
WHERE Holiday IS NOT NULL
If that's the case, you don't really need the function any more. You can instead use a CASE expression.

Top 12 from union queries filtering max 12 rows Ms Sql

I am facing issue, that I need 12 max rows from 3 tables union data. But if any of the table do not have fixed rows than other table should be considered for remaining rows.
For example: I have 3 tables
Product
Category
Manufacturer
Case 1 : If all have 10 rows each than we should select 4 rows from each table.
Case 2: If product table have 3 rows and category an manufacturer have 5 rows
each, than it should select 3 from product and 5 from category or manufacturer each.
Case 3: All have less than 4 rows than shows as many rows available.
Please help me to acheive this. I am posting my Stored Procedure which I have created till now.
Create PROCEDURE [dbo].[GetRowsfromtables]
#SearchTerms nvarchar(150)
AS
BEGIN
create table #search (id int identity ,ids int, productname nvarchar(200), categoryname nvarchar(200), tagname nvarchar(200), SeName nvarchar(200), displayorder int)
insert into #search
Select Top 12 p.Id as ids, p.name as productname, '' as categoryname, '' as tagname, '' as SeName, 0 as displayorder from Product p where p.Published = 1 and p.Deleted = 0 and name like '%' + #SearchTerms + '%'
union
Select Top 12 c.id as ids, '' as productname, c.name as categoryname, '' as tagname, '' as SeName , 1 as displayorder from Category c where c.Published = 1 and c.Deleted = 0 and Name like '%' + #SearchTerms + '%'
union
Select Top 12 t.id as ids, '' as productname, '' as categoryname, t.Name as tagname, '' as SeName, 2 as displayorder
from Manufacturer
where Name like '%' + #SearchTerms + '%'
Select id, ids,productname,categoryname, tagname, SeName, tagpcount,tagproductid,
'' as ThumbnailImage, displayorder, (Select count(*) from #search where displayorder = 0) as ptotal,
(Select count(*) from #search where displayorder = 1) as ctotal,
(Select count(*) from #search where displayorder = 2) as tagtotal,
row_number() over(partition by displayorder order by id ) as rn
from #search
order by displayorder
drop table #search
END
You can use the windowed function ROW_NUMBER to sequence the records within the union. Then order the result by the row number:
/* Generate 13 sample records:
* Product (4)
* Category (6)
* Manufacturer (3)
*/
WITH Product AS
(
-- Sample product records.
SELECT
Id
FROM
(
VALUES
(1),
(2),
(3),
(4)
) AS x(Id)
),
Category AS
(
-- Sample category records.
SELECT
Id
FROM
(
VALUES
(1),
(2),
(3),
(4),
(5),
(6)
) AS x(Id)
),
Manufacturer AS
(
-- Sample manufacturer records.
SELECT
Id
FROM
(
VALUES
(1),
(2),
(3)
) AS x(Id)
)
SELECT TOP 12
*
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS rn,
'P' AS Tbl,
Id
FROM
Product
UNION ALL
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS rn,
'C' AS Tbl,
Id
FROM
Category
UNION ALL
SELECT
ROW_NUMBER() OVER (ORDER BY ID) AS rn,
'M' AS Tbl,
Id
FROM
Manufacturer
) AS u
ORDER BY
rn
;
The order by will return all the records numbered 1 first, then 2 and so on. This will provide as even a mix as the source data allows.
EDIT
Removed TOP from each of the subqueries. I was trying to help SQL out by limiting the number of records it had to apply a row number to. But the ROW_NUMBER is applied before the top, making this a somewhat pointless exercise. My subqueries also lacked an ORDER BY clause. Without this there is no guaranteed order, making "top" a pointless statement.
As mentioned in the comments you need to have an ORDER BY when using TOP or you will have no way of determining which top rows you will get. There also is not really a need to create a temp table here so you can select from it and then drop it. I added some (ok well a lot) of white space around your original so this is easier to see and maintain down the road. Something like this should be really close to what you after.
One thing important is you have a couple columns defined in your original query that were not in your defined temp table. No idea what you want to do with those so I just left them there.
Select top 12 id
, ids
, productname
, categoryname
, tagname
, SeName
, tagpcount
, tagproductid
, '' as ThumbnailImage
, displayorder
, ptotal = sum(case when displayorder = 0 then 1 else 0 end)
, ctotal = sum(case when displayorder = 1 then 1 else 0 end)
, tagtotal = sum(case when displayorder = 2 then 1 else 0 end)
, row_number() over(partition by displayorder order by id ) as rn
from
(
Select Top 12 p.Id as ids
, p.name as productname
, '' as categoryname
, '' as tagname
, '' as SeName
, 0 as displayorder
from Product p
where p.Published = 1
and p.Deleted = 0
and name like '%' + #SearchTerms + '%'
ORDER BY p.name --or whatever column
union ALL
Select Top 12 c.id as ids
, '' as productname
, c.name as categoryname
, '' as tagname
, '' as SeName
, 1 as displayorder
from Category c
where c.Published = 1
and c.Deleted = 0
and Name like '%' + #SearchTerms + '%'
ORDER BY c.name --or whatever column
union ALL
Select Top 12 t.id as ids
, '' as productname
, '' as categoryname
, t.Name as tagname
, '' as SeName
, 2 as displayorder
from Manufacturer
where Name like '%' + #SearchTerms + '%'
ORDER BY t.Name --or whatever column
) x
group by id
, ids
, productname
, categoryname
, tagname
, SeName
, tagpcount
, tagproductid
, displayorder
order by displayorder
In each of your queries you could determine row_number() based on the sort order currently used for TOP n - then you do a CASE based on the row_number
CASE WHEN row_number() OVER(...) <= 4 THEN 1 ELSE 2 END AS sorter
Remove the TOP n in each case.
After you have done your union of 3 tables, take the TOP 12 from the UNION as a subquery, and order it by sorter ASC, NEWID(). That way you are guaranteed to have any 4 of the tables where they exist, and randoms from other tables to make up the numbers
e.g.
SELECT TOP 12 DQ.* FROM ('{your query}') DQ ORDER DQ.sorter ASC,newid()

Trying to pivot event dates in t-sql without using a cursor

I have the following table:
What I want is to get to this:
EventTypeId 1 and 3 are valid start events and EventTypeId of 2 is the only valid end event.
I have tried to do a pivot, but I don't believe a pivot will get me the multiple events for a person in the result set.
SELECT PersonId, [1],[3],[2]
FROM
(
SELECT PersonId, EventTypeId, EventDate
from #PersonEvent
) as SourceTable
PIVOT
(
count(EventDate) FOR EventTypeId
IN ([1],[3],[2])
) as PivotTable
Select PersonID,
Min(Case WHEN EventTypeId IN (1,3) THEN EventDate END) as StartDate,
Min(Case WHEN EventTypeId IN (2) THEN EventDate END) as EndDate
FROM #PersonEvent
group by personid
I can do a cursor, but my original table is over 90,000 rows, and this is to be for a report, so I don't think I can use that option. Any other thoughts that I might be missing?
Assuming the table is called [dbo].[PersonEventRecords] this will work...
With StartEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (1,3)
), EndEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (2)
)
Select IsNull(se.PersonId,ee.PersonId) As PersonId,
se.EventTypeId As StartEventTypeId,
se.EventDate As StartEventDate,
ee.EventTypeId As EndEventTypeId,
ee.EventDate As EndEventDate
From StartEvents se
Full Outer Join EndEvents ee
On se.PersonId = ee.PersonId
And se.EventSequence = ee.EventSequence - 1
Order By IsNull(se.PersonId,ee.PersonId),
IsNull(se.EventDate,ee.EventDate);
/**** TEST DATA ****/
If Object_ID('[dbo].[PersonEventRecords]') Is Not Null
Drop Table [dbo].[PersonEventRecords];
Create Table [dbo].[PersonEventRecords]
(
PersonId Int,
EventTypeId Int,
EventDate Date,
EventSequence Int
);
Insert [dbo].[PersonEventRecords]
Select 1,1,'2012-10-13',1
Union All
Select 1,2,'2012-10-20',2
Union All
Select 1,1,'2012-11-01',3
Union All
Select 1,2,'2012-11-13',4
Union All
Select 2,1,'2012-05-07',1
Union All
Select 2,2,'2012-06-01',2
Union All
Select 2,3,'2012-07-01',3
Union All
Select 2,2,'2012-08-30',4
Union All
Select 3,2,'2012-04-05',1
Union All
Select 3,1,'2012-05-04',2
Union All
Select 3,2,'2012-05-24',3
Union All
Select 4,1,'2013-01-03',1
Union All
Select 4,1,'2013-02-20',2
Union All
Select 4,2,'2013-03-20',3;
Try this
SELECT E1.PersonId, E1.EventTypeId, E1.EventDate, E2.EventTypeId, E2.EventDate
FROM PersonEvent AS E1
OUTER APPLY(
SELECT TOP 1 PersonEvent.EventTypeId, PersonEvent.EventDate
FROM PersonEvent
WHERE PersonEvent.PersonId = E1.PersonId
AND PersonEvent.EventSequence = E1.EventSequence + 1
AND PersonEvent.EventTypeId = 2
) AS E2
WHERE E1.EventTypeId = 1 OR E1.EventTypeId = 3
UNION
SELECT E3.PersonId, NULL, NULL, E3.EventTypeId, E3.EventDate
FROM PersonEvent E3
WHERE E3.EventTypeId = 2
AND NOT EXISTS(
SELECT *
FROM PersonEvent
WHERE PersonEvent.PersonId = E3.PersonId
AND PersonEvent.EventSequence = E3.EventSequence - 1)
It is not completely clear how do you want the result to be ordered – add order as needed.

How to structure SQL Statement

I have a table that contains a list of tasks;
TableName: Tasks. Fields: (ID Int, Description nvarchar)
The tasks are completed daily and are logged in a table like follows;
TableName TasksDone. Fields: (TaskID Int, TaskDate DateTime)
I need to have a query that runs for a date range and shows the tasks that were NOT done (do not exist in the TasksDone table) for every date in the range.
I hope that makes sense...
Thanks for any help.
You will need a numbers or calendar table to make things easy, or we can simulate one if the range is small. Is the TaskDate a plain date, or does it have a time component also?
Basic plan of attack is:
declare #StartDate datetime
declare #EndDate datetime
/* Set #StartDate and #EndDate to represent the range */
with Digits as (
select 0 as d union all select 1 union all select 2 union all select 3 union all select 4 union all
select 5 union all select 6 union all select 7 union all select 8 union all select 9
), Numbers as (
select (D1.d * 100) + (D2.d * 10) + D3.d as n
from Digits D1,Digits D2,Digits D3
), TaskDates as (
select
t.TaskID,
DATEADD(day,n.n,#StartDate) as TaskDate
from
Tasks t
inner join
Numbers n
on
DATEADD(day,n.n,#StartDate) <= #EndDate
)
select
*
from
TaskDates td1
left join
TasksDone td2
on
td1.TaskID = td2.TaskID and
DATEDIFF(day,td1.TaskDate,td2.TaskDate) = 0
where
td2.TaskID is null
The first two CTEs build a small numbers table, the 3rd CTE constructs a set of TaskIDs and Dates within the required range. The final select matches theses against the TasksDone table, and then discards those rows where a match is found. If TasksDone.TaskDate is a plain date (no time component) and #StartDate is also with no time component, then you can ditch the DATEDIFF and just use td1.TaskDate = td2.TaskDate.
If you need a large range (above can cover ~3 years), I'd suggest building a proper number table or calendar table
This is fairly straight forward, if I'm understanding the problem correctly:
SELECT *
FROM Tasks
WHERE ID NOT IN (SELECT TaskID FROM TasksDone WHERE TaskDate BETWEEN x AND y)
Replace x and y with the date you're after.
I've not tested this out but see if this helps:
select ID, TaskDate as A from Tasks,TasksDone
where TaskID not in (select TaskID from TasksDone where TaskDate = A)
GROUP BY TaskDate
If I understand correct, following statement should get you the tasks that didn't get executed every day in the entire range.
SQL Statement
SELECT t.*
FROM #Tasks t
INNER JOIN (
SELECT TaskID
FROM #TasksDone td
WHERE td.TaskDate BETWEEN #RangeStart AND #RangeEnd
GROUP BY
td.TaskID
HAVING COUNT(TaskID) < CAST(#RangeEnd - #RangeStart AS INTEGER)+1
UNION ALL
SELECT TaskID
FROM #TasksDone td
WHERE TaskID NOT IN (SELECT TaskID
FROM #TasksDone
WHERE TaskDate BETWEEN #RangeStart AND #RangeEnd)
) td ON td.TaskID = t.ID
Test script
DECLARE #Tasks TABLE (
ID INTEGER
, DESCRIPTION NVARCHAR(32)
)
DECLARE #TasksDone TABLE (
TaskID INTEGER
, TaskDate DATETIME
)
DECLARE #RangeStart DATETIME
DECLARE #RangeEnd DATETIME
SET #RangeStart = GetDate() - 1
SET #RangeEnd = GetDate() + 1
INSERT INTO #Tasks
SELECT 1, 'Done Every Day in range.'
UNION ALL SELECT 2, 'Done a few times in range.'
UNION ALL SELECT 3 , 'Not done anytime in range.'
INSERT INTO #TasksDone
SELECT 1, #RangeStart
UNION ALL SELECT 1, GetDate()
UNION ALL SELECT 1, #RangeEnd
UNION ALL SELECT 2, GetDate()
UNION ALL SELECT 3, GetDate() + 2
SELECT t.*
FROM #Tasks t
INNER JOIN (
SELECT TaskID
FROM #TasksDone td
WHERE td.TaskDate BETWEEN #RangeStart AND #RangeEnd
GROUP BY
td.TaskID
HAVING COUNT(TaskID) < CAST(#RangeEnd - #RangeStart AS INTEGER)+1
UNION ALL
SELECT TaskID
FROM #TasksDone td
WHERE TaskID NOT IN (SELECT TaskID FROM #TasksDone WHERE TaskDate BETWEEN #RangeStart AND #RangeEnd)
) td ON td.TaskID = t.ID

Resources