Workaround for PIVOT statement - sql

I have this query, is taking like 2 minutes to resolve, I need to find a workaround, I know that UNPIVOT has a better solution using CROSS APPLY, is there anything similar for PIVOT?
SELECT [RowId], [invoice date], [GL], [Entité], [001], [Loc], [Centre Cout], [Compte_1], [Interco_1], [Futur_1], [Department], [Division], [Compagnie], [Localisation], [Centre/Cout], [Compte], [Interco], [Futur], [Account], [Mobile], [Last Name], [First Name], [license fee], [GST], [HST], [PST], [Foreign Tax], [Sales Tax License], [Net Total], [Total], [ServiceType], [Oracle Cost Center], [CTRL], [EXPENSE], [Province]
FROM
(SELECT fd.[RowId], fc.[ColumnName], fd.[Value]
FROM dbo.FileData fd
INNER JOIN dbo.[FileColumn] fc
ON fc.[FileColumnId] = fd.[FileColumnId]
WHERE FileId = 1
AND TenantId = 1) x
PIVOT
(
MAX(Value)
FOR [ColumnName] IN ( [invoice date], [GL], [Entité], [001], [Loc], [Centre Cout], [Compte_1], [Interco_1], [Futur_1], [Department], [Division], [Compagnie], [Localisation], [Centre/Cout], [Compte], [Interco], [Futur], [Account], [Mobile], [Last Name], [First Name], [license fee], [GST], [HST], [PST], [Foreign Tax], [Sales Tax License], [Net Total], [Total], [ServiceType], [Oracle Cost Center], [CTRL], [EXPENSE], [Province])
) AS p

Pivots are great, but so are Conditional Aggregations. Also, there would be no datatype conficts or conversions necessary
SELECT [RowId]
,[invoice date] = max(case when [FileColumnId] = ??? then Value end)
,[GL] = max(case when [FileColumnId] = ??? then Value end)
,... more fields
FROM dbo.FileData fd
WHERE FileId = 1
AND TenantId = 1
Group By [RowId]
EDIT
You could add back the join to make it more readable.

Related

Why does my record count increase as my denominator for a division performed in a nested query increases?

In my query I'm trying to calculate the total overtime worked by employees by dividing basic salary by 173(hours in a month) which gives me the hourly rate then dividing the total overtime amount of employee by the hourly rate. To my surprise the record count increases as the number of hours in a month increases, should it not decrease?
Here's my script:
select 'Employees that worked more overtime' [CAAT],*
from ( select H.[Month]
,H.[Employee Code]
,H.[Department]
,H.[Job title]
,H.[Surname]
,H.[Full Names]
,H.[Basic Salary]
,R.[Overtime]
,H.[Hourly Rate]
,round(R.[Overtime] / H.[Hourly Rate],2) [Overtime Hours]
from (select [Month]
,[Employee Code]
,Department
,[Job title]
,[Surname]
,[Full Names]
,nullif(convert(money,[Amount]),0.00) [Basic Salary]
,nullif(round(convert(money,[Amount]) / 173,2),0.00) [Hourly Rate]
from [Salary DB]
where [Field Desc] = 'ED01-Basic Salary') H
left join
(select [Month]
,[Employee Code]
,nullif(sum(convert(money,[Amount])),0.00) [Overtime]
from [Salary DB]
where [Field Desc] in ('ED02-O/Time 1.5','ED02-O/Time 2.0','ED42-Sunday Pay')
group by [Month]
,[Employee Code]) R
on H.[Employee Code] = R.[Employee Code]
and H.[Month] = R.[Month]) [Data]
where [Overtime Hours] > '40'
Order by [Employee Code], [Month] Desc

optimize the query which results the duplicates taken from two diff data tables

select
A.SlNo
,A.[Company Code]
,A.[Document Number]
,A.[Document Type]
,CONVERT(varchar(10),A.[Document Date],101) AS [Document Date]
,A.[Reference]
,CAST(Null as nvarchar(5)) As NumericInvoice
,CONVERT(varchar(10),A.[Posting Date],101) AS [Posting Date]
,A.[Clearing Document]
,A.[Amount in doc# curr#]`enter code here`
,A.[Document currency]
,A.[Account]
,A.[Account Description]
,A.[User Name]
,A.dumptype
,CAST(Null As Varchar(5)) As Similarity
,CASE
When (A.[Reference] = B.[Reference] and A.[Amount in doc# curr#]= B.[Amount in doc# curr#]) then 'DUPEPAYA'
END As [Status]
,CAST(Null As Varchar(5)) As [DupeSets]
from MasterData A
Join InputData B on (A.[Reference] = B.[Reference] and A.[Amount in doc# curr#]= B.[Amount in doc# curr#])
Union All
select distinct
A.SlNo
,A.[Company Code]
,A.[Document Number]
,A.[Document Type]
,CONVERT(varchar(10),A.[Document Date],101) AS [Document Date]
,A.[Reference]
,CAST(Null as nvarchar(5)) As NumericInvoice
,CONVERT(varchar(10),A.[Posting Date],101) AS [Posting Date]
,A.[Clearing Document]
,A.[Amount in doc# curr#]
,A.[Document currency]
,A.[Account]
,A.[Account Description]
,A.[User Name]
,A.dumptype
,CAST(Null As Varchar(5)) As Similarity
,CASE
When (A.[Reference] = B.[Reference] and A.[Amount in doc# curr#]= B.[Amount in doc# curr#]) then 'DUPEPAYA'
END As [Status]
,DENSE_Rank () Over (order by A.[Reference], A.[Account Description], A.dumptype asc) As [DupeSets]
from InputData A
Join MasterData B on (A.[Reference] = B.[Reference] and A.[Amount in doc# curr#]= B.[Amount in doc# curr#])
order by A.[Reference], A.[Account Description], A.dumptype asc
I want to optimize the above query which results in the duplicates from masterdata table to that of the input data table on few validations, these data tables deal with a huge amount of data hence taking a long time to execute the query. please help
Note: I'm a beginner in SQL.

T-SQL [UNION ALL] removing records from query result

Have a simple UNION ALL query marrying the results of two queries. The first query, run independently, returns 1208 records and the second 14. I would expect a properly syntaxed UNION ALL to return 1222 records but mine falls to 896.
Makes zero sense to me:
SELECT a.WBS_ELEMENT_ID as [WBS Element],
a.WBS_ELEMENT_DESC as [WBS Element Desc],
a.UHC_INDUSTRY as [Industry],
a.UHC_SECTOR as [Sector],
a.UHC_DUNS_NUMBER as [UHC DUNS Number],
a.UHC_DUNS_NAME as [UHC DUNS Name],
a.PRIORITY_SUB_SECTOR as [Priority Sub Sector],
a.BUDGET_ALLOCATION as [Budget Allocation],
a.LAST_UPDATED_ON as [Last Updated]
FROM DimSectorPd a
WHERE a.wbs_element_id is not null
UNION ALL
SELECT ROW_NUMBER() OVER (ORDER BY a.wbs_element_desc) as [WBS Element],
a.WBS_ELEMENT_DESC as [WBS Element name],
a.UHC_INDUSTRY as [Industry],
a.UHC_SECTOR as [Sector],
a.UHC_DUNS_NUMBER as [UHC DUNS Number],
a.UHC_DUNS_NAME as [UHC DUNS Name],
a.PRIORITY_SUB_SECTOR as [Priority Sub Sector],
a.BUDGET_ALLOCATION as [Budget Allocation],
a.LAST_UPDATED_ON as [Last Updated]
from dimsectorpd a where a.WBS_ELEMENT_ID is null
Your queries should return all rows in the table. Unless the table changes between executions, the results from running the subqueries separately should be the same as from running them with the UNION ALL.
As a note, if you want to simplify the query, then you can do:
SELECT COALESCE(a.WBS_ELEMENT_ID,
ROW_NUMBER() OVER (PARTITION BY wbs_element_id ORDER BY a. wbs_element_desc)
) as [WBS Element],
a.WBS_ELEMENT_DESC as [WBS Element Desc],
a.UHC_INDUSTRY as [Industry],
a.UHC_SECTOR as [Sector],
a.UHC_DUNS_NUMBER as [UHC DUNS Number],
a.UHC_DUNS_NAME as [UHC DUNS Name],
a.PRIORITY_SUB_SECTOR as [Priority Sub Sector],
a.BUDGET_ALLOCATION as [Budget Allocation],
a.LAST_UPDATED_ON as [Last Updated]
FROM DimSectorPd a;
Obviously there is nothing wrong with your syntax, but if you want to try a different approach to getting your UNION ALL to work with ROW_NUMBER. Here it is:
;WITH q1
AS (
SELECT a.WBS_ELEMENT_ID AS [WBS Element]
,a.WBS_ELEMENT_DESC AS [WBS Element Desc]
,a.UHC_INDUSTRY AS [Industry]
,a.UHC_SECTOR AS [Sector]
,a.UHC_DUNS_NUMBER AS [UHC DUNS Number]
,a.UHC_DUNS_NAME AS [UHC DUNS Name]
,a.PRIORITY_SUB_SECTOR AS [Priority Sub Sector]
,a.BUDGET_ALLOCATION AS [Budget Allocation]
,a.LAST_UPDATED_ON AS [Last Updated]
FROM DimSectorPd a
WHERE a.wbs_element_id IS NOT NULL
UNION ALL
SELECT b.WBS_ELEMENT_ID AS [WBS Element] --just bring NULL values
,b.WBS_ELEMENT_DESC AS [WBS Element name]
,b.UHC_INDUSTRY AS [Industry]
,b.UHC_SECTOR AS [Sector]
,b.UHC_DUNS_NUMBER AS [UHC DUNS Number]
,b.UHC_DUNS_NAME AS [UHC DUNS Name]
,b.PRIORITY_SUB_SECTOR AS [Priority Sub Sector]
,b.BUDGET_ALLOCATION AS [Budget Allocation]
,b.LAST_UPDATED_ON AS [Last Updated]
FROM dimsectorpd b
WHERE b.WBS_ELEMENT_ID IS NULL
)
SELECT CASE
WHEN q1.[WBS Element] IS NULL
THEN ROW_NUMBER() OVER (ORDER BY q1.WBS_Element_Desc)
ELSE q1.[WBS Element]
END [WBS_Element]
,q1.[WBS Element Desc]
,q1.Industry
,q1.Sector
,q1.[UHC DUNS Number]
,q1.[UHC DUNS Name]
,q1.[Priority Sub Sector]
,q1.[Budget Allocation]
,q1.[Last Updated]
FROM q1
Here is a simplified example can you see if it works on your server?
SELECT a.low AS [My ID],
a.name AS [My Letter]
FROM master..spt_values as a
WHERE low is not null
UNION ALL
SELECT ROW_NUMBER() OVER (ORDER BY a.name) AS [My ID],
a.name AS [My Letter]
FROM master..spt_values as a
WHERE a.low is null
master..spt_values as 2515 rows on my system...

only return records count() = 1

I have this table called myTable
Posting Date|Item No_|Entry Type|
2015-01-13|1234|1
2015-01-13|1234|1
2015-01-12|1234|1
2015-01-12|5678|1
2015-02-12|4567|1
What I want, is only return result where a [Item No_] is ind the table 1 time.
So in this example of my table, i only want to return [Item No_] 5678 and 4567, because there only are one record in it. And then ignore [Item No_] 1234
This is my SQL i have tried, but something is wrong. Can anyone help me?
SELECT [Item No_], [Posting Date], COUNT([Item No_]) AS Antal
FROM myTable
GROUP BY [Entry Type], [Posting Date], [Item No_]
HAVING ([Entry Type] = 1) AND (COUNT([Item No_]) = 1)
ORDER BY [Posting Date] DESC
select [Item No_]
from myTable
group by [Item No_]
having count(*)=1
Remove Posting Date from group by
SELECT [Item No_],Entry Type, COUNT([Item No_]) AS Antal
FROM myTable
GROUP BY [Entry Type], [Item No_]
HAVING COUNT([Item No_]) = 1
or if you want other details use a subquery
SELECT [Item No_],
Entry Type,
Posting Date
FROM myTable a
WHERE EXISTS (SELECT 1
FROM myTable b
where a.[Item No_]=b.[Item No_]
GROUP BY [Entry Type],
[Item No_]
HAVING Count(1) = 1)
ORDER BY [Posting Date] DESC
or window function
;WITH cte
AS (SELECT [Item No_],
[Posting Date],
[Entry Type],
Row_number()OVER (Partition BY [Entry Type], [Item No_] ORDER BY [Item No_]) RN
FROM myTable)
SELECT *
FROM cte a
WHERE NOT EXISTS (SELECT 1
FROM cte b
WHERE a.[Item No_] = b.[Item No_]
AND rn > 1)
ORDER BY [Posting Date] DESC
You can use ROW_Number() in Sql Server
select * from (
SELECT [Item No_], [Posting Date],
Row_Number() over (Parition by [Item No_]
order by [Item No]) RN
FROM myTable
)D
where D.RN=1

Find the most recent row of a group

I know there is no "last" row so I hope I'm clear that isn't what I'm really looking for. I want to select the rows in a table if the value of one particular field is the last alphabetically. I'll try my best to draw it out below. I'm a bit of a novice so please bear with me...
TABLE
[Order Number], [Delivery Date], [Order Qty], [Shipped Qty], [Bill To], [Ship To], [Invoice Number]
There are many times when we will reissue invoices and that invoice number will increment by a letter. This will also update additional field values as well. Below is a typical set with multiples invoices...
'987654', '2014-05-01 00:00:00', '100', '90', 'BillToXYZ', 'ShipToXYZ', '987654A' - NEW RECORD -
'987654', '2014-05-01 00:00:00', '-100', '-90', 'BillToXYZ', 'ShipToXYZ', '987654B' - NEW RECORD -
'987654', '2014-05-01 00:00:00', '100', '100', 'BillToXYZ', 'ShipToNEWSHIPTOLOCATION', '987654C' - NEW RECORD -
'987654', '2014-05-01 00:00:00', '10', '10', 'BillToXYZ', '2ndNEWSHIPTOLOCATION', '987654D' - NEW RECORD -
What I need is to query all the above fields and only bring back those where the [Invoice Number] is the last(alphabetically) (in this case 987654D) but also have it SUM the values of the [Order Qty] and [Shipped Qty] for all of the records regardless of [Invoice Number].
If I can provide any additional information please let me know. Thank you in advance.
It possible to use the ROW_NUMBER function to get the last row in a group setting the ORDER BY descending and then using a filter to get the row with the value 1.
The SUM and MAX with windowing help to get the other aggregate values.
WITH D AS (
SELECT [Order Number]
, [Delivery Date]
, SUM([Order Qty]) OVER (PARTITION BY [Order Number]) [Total Order Qty]
, [Total Shipped Qty]
= SUM([Shipped Qty]) OVER (PARTITION BY [Order Number])
, [Bill To]
, [Ship To]
, [Last Invoice Number]
= MAX([Invoice Number]) OVER (PARTITION BY [Order Number])
, ID = ROW_NUMBER() OVER (PARTITION BY [Order Number]
ORDER BY [Invoice Number] DESC)
FROM Table1
)
SELECT [Order Number]
, [Delivery Date]
, [Total Order Qty]
, [Total Shipped Qty]
, [Bill To]
, [Ship To]
, [Last Invoice Number]
FROM D
WHERE ID = 1
SQLFiddle demo
Looks to me like you can use a GROUP BY and aggregates as suggested by other answers, but put that in a subquery so that you can link your 'main' table to it and get the detail values for the latest invoice. Does the following code do what you're looking for?
CREATE TABLE #Table
(
OrderNumber int,
DeliveryDate datetime,
OrderQty int,
ShippedQty int ,
BillTo varchar(10),
ShipTo varchar(100),
InvoiceNumber varchar(20)
)
INSERT INTO #Table
(
OrderNumber,
DeliveryDate,
OrderQty,
ShippedQty,
BillTo,
ShipTo,
InvoiceNumber
)
SELECT '987654', '2014-05-01 00:00:00', '100', '90', 'BillToXYZ', 'ShipToXYZ', '987654A' UNION
SELECT '987654', '2014-05-01 00:00:00', '-100', '-90', 'BillToXYZ', 'ShipToXYZ', '987654B' UNION
SELECT '987654', '2014-05-01 00:00:00', '100', '100', 'BillToXYZ', 'ShipToNEWSHIPTOLOCATION', '987654C' UNION
SELECT '987654', '2014-05-01 00:00:00', '10', '10', 'BillToXYZ', '2ndNEWSHIPTOLOCATION', '987654D'
SELECT t.*, s.TotalOrderQty, s.TotalShippedQty
FROM
#Table t
INNER JOIN
(
SELECT
OrderNumber,
MAX(InvoiceNumber) LastInvoice,
SUM(OrderQty) TotalOrderQty,
SUM(ShippedQty) TotalShippedQty
FROM #Table
GROUP BY OrderNumber
) s ON
t.OrderNumber = s.OrderNumber AND
t.InvoiceNumber = s.LastInvoice
DROP TABLE #Table
Is this what you're going after?
EDIT 2: Got clarification from OP. Have edited my solution to bring up aggregation for complete order but only the line item information of the latest invoice. Fiddle here: http://sqlfiddle.com/#!3/08bb0/6
SELECT t1.[Order Number],
t2.[Max Invoice Number],
t1.[Ship To],
t2.[Sum Order Qty],
t2.[Sum Shipped Qty]
FROM Table1 t1 INNER JOIN
(SELECT [Order Number],
MAX([Invoice Number] ) AS [Max Invoice Number],
SUM([Order Qty]) AS [Sum Order Qty],
SUM([Shipped Qty]) AS [Sum Shipped Qty]
FROM Table1
GROUP BY [Order Number]) t2
ON t1.[Invoice Number] = t2.[Max Invoice Number]
ORDER BY t1.[Order Number];
You can add more columns from the D invoice by placing them in the SELECT list from t1, as I did with [Ship To].
EDIT 1: Added grouping so whole table is considered. To be fair, Martin K. posted this answer first.