Syntax errors with UNPIVOT and JOIN in FROM clause - sql-server

I'm using SQL Server Express (with advanced services), version 10.50.1600.1.
I need to use a UNPIVOT to create multiple rows from a single ROW, and also need to use JOINS, to get fields from master data tables.
I can make 2 working but separate SELECT statements. The first is the UNPIVOT, the second contains the JOINs. But I simply cannot make them work together! When I put the UNPIVOT followed by the JOINs, I'm always getting error 8156 (Column was specified multiple times) or 4104 (The multi-part identifier could not be bound)...
Sample data is in this SQL Fiddle: http://sqlfiddle.com/#!2/452de/1
Here's the sample data
Table TIMESHEET_LINE
PROJECT_ID DATE1 DATE7 HOUR1 HOUR2 HOUR3 HOUR4 HOUR5 HOUR6 HOUR7
16 2011-10-03 2011-10-09 0 0 0,5 0 0 0 0
18 2011-10-03 2011-10-09 0 0 0,01111111 0 0 0 0
18 2011-10-03 2011-10-09 0 0 0,001944444 0 0 0 0
28 2011-10-03 2011-10-09 0 0 0 2 0 0 0
13 2011-10-03 2011-10-09 0 0 0 0 0 0 0
18 2011-10-03 2011-10-09 0 0 0 0 1,250556 0 0
18 2011-10-03 2011-10-09 0 0 0 0 0,7141666 0 0
Table Project
Table PROJECT
PROJECT_ID PROJECT_NUMBER PROJECT_NAME
13 30013 Control Venta Negativa
16 24464 Zonas de Transporte
18 PRBRCOM2012_12 Garantia
28 24466 Embalagens Retornáveis
The expected output is:
PROJECT_NUMBER PROJECT_NAME DATE HOUR
30013 Control Venta Negativa 2011-10-03 0
30013 Control Venta Negativa 2011-10-04 0
30013 Control Venta Negativa 2011-10-05 0,5
30013 Control Venta Negativa 2011-10-06 0
30013 Control Venta Negativa 2011-10-07 0
30013 Control Venta Negativa 2011-10-08 0
30013 Control Venta Negativa 2011-10-09 0
PRBRCOM2012_12 Garantia 2011-10-03 0
PRBRCOM2012_12 Garantia 2011-10-04 0
PRBRCOM2012_12 Garantia 2011-10-05 0,01111111
PRBRCOM2012_12 Garantia 2011-10-06 0
PRBRCOM2012_12 Garantia 2011-10-07 0
PRBRCOM2012_12 Garantia 2011-10-08 0
PRBRCOM2012_12 Garantia 2011-10-09 0
The SQL statement to UNPIVOT the TIMESHEET_LINE table:
SELECT
[USER_ID],
[PROJECT_ID],
[TASK_GROUP_ID],
[TASK_ID],
DATEADD
(
DAY,
CAST( RIGHT([WeekDay],1)
AS int) - 1,
Date1
) As 'Date',
SUM(Hours) AS 'Hours'
FROM [aceproject].[dbo].[TIMESHEET_LINE]
UNPIVOT
(
Hours for [WeekDay] in (HOUR1, HOUR2, HOUR3, HOUR4, HOUR5, HOUR6, HOUR7)
) upvt
WHERE
[USER_ID] = '18'
GROUP BY
[USER_ID],
[PROJECT_ID],
[TASK_GROUP_ID],
[TASK_ID],
[WeekDay],
[Date1]
The SQL Statement to JOIN the tables:
SELECT
TSL.PROJECT_ID,
P.PROJECT_NUMBER,
P.PROJECT_NAME
FROM [TIMESHEET_LINE] AS TSL
INNER JOIN [aceproject].[dbo].[PROJECT] AS P with (nolock) ON P.PROJECT_ID = TSL.PROJECT_ID
I tried to put them together on several ways. First by using JOINs and UNPIVOT in the same SELECT statement:
SELECT
P.PROJECT_NUMBER,
P.PROJECT_NAME,
TSL.[USER_ID],
TSL.[PROJECT_ID],
TSL.[TASK_GROUP_ID],
TSL.[TASK_ID],
DATEADD
(
DAY,
CAST( RIGHT(upvt.[WeekDay],1)
AS int) - 1,
TSL.Date1
) As 'Date',
SUM(upvt.Hours) AS 'Hours'
FROM [TIMESHEET_LINE] AS TSL
INNER JOIN PROJECT AS P with (nolock) ON P.PROJECT_ID = TSL.PROJECT_ID
UNPIVOT
(
Hours for [WeekDay] in (HOUR1, HOUR2, HOUR3, HOUR4, HOUR5, HOUR6, HOUR7)
) upvt
WHERE
TSL.[USER_ID] = '18'
GROUP BY
TSL.[USER_ID],
TSL.[PROJECT_ID],
TSL.[TASK_GROUP_ID],
TSL.[TASK_ID],
upvt.[WeekDay],
TSL.[Date1]
Also tried selecting from the separate statements:
SELECT
Project_ID,
Client_Country,
Project_Create_By,
Resource_Country,
Resource_IPN,
DATEADD
(
DAY,
CAST( RIGHT([WeekDay],1)
AS int) - 1,
Date1
) As 'Date',
Hours AS 'Hours'
FROM
(
SELECT
TSL.[USER_ID],
TSL.[PROJECT_ID],
TSL.[TASK_GROUP_ID],
TSL.[TASK_ID],
TSL.PROJECT_ID AS 'Project_ID',
Left(C.CLIENT_NAME,2) AS 'Client_Country',
LTRIM(PU.USERNAME) AS 'Project_Create_By',
LEFT(UG1.USER_GROUP_NAME,2) AS 'Resource_Country',
LTRIM(U.USERNAME) AS 'Resource_IPN',
TSL.DATE1,
TSL.HOUR1,
TSL.HOUR2,
TSL.HOUR3,
TSL.HOUR4,
TSL.HOUR5,
TSL.HOUR6,
TSL.HOUR7
FROM [aceproject].[dbo].[TIMESHEET_LINE] AS TSL
INNER JOIN [aceproject].[dbo].[PROJECT] AS P with (nolock) ON P.PROJECT_ID = TSL.PROJECT_ID
LEFT JOIN CLIENT AS C with (nolock) ON C.CLIENT_ID = P.CLIENT_ID
LEFT JOIN USERS AS PU with (nolock) ON (PU.COMPANY_ID = P.COMPANY_ID and PU.USER_ID = P.PROJECT_CREATOR_ID)
LEFT JOIN USERS AS U with (nolock) ON U.USER_ID = TSL.USER_ID
LEFT JOIN USER_GROUP AS UG1 with (nolock) ON (UG1.COMPANY_ID = U.COMPANY_ID and UG1.USER_GROUP_ID = U.USER_GROUP_ID)
) d
UNPIVOT
(
Hours for [WeekDay] in (HOUR1, HOUR2, HOUR3, HOUR4, HOUR5, HOUR6, HOUR7)
) upvt
Also tried the opposite, with the UNPIVOT as sub SELECT.
But nothing worked.
Thanks in advance!

Ok, I sorted it out, finally! =)
First you make the JOIN, and then you make a nested select over it:
SELECT TOP 100
TIMESHEET_LINE_ID,
PROJECT_NUMBER,
DATEADD
(
DAY,
CAST( RIGHT([WeekDay],1)
AS int) - 1,
Date1
) As 'Date',
Hours AS 'Hours'
FROM
(
SELECT
P.PROJECT_NUMBER,
TSL.TIMESHEET_LINE_ID,
TSL.DATE1,
TSL.HOUR1,
TSL.HOUR2,
TSL.HOUR3,
TSL.HOUR4,
TSL.HOUR5,
TSL.HOUR6,
TSL.HOUR7
FROM
[aceproject].[dbo].[TIMESHEET_LINE] AS TSL
INNER JOIN PROJECT AS P with (nolock) ON P.PROJECT_ID = TSL.PROJECT_ID
) d
UNPIVOT
(
Hours for [WeekDay] in (HOUR1, HOUR2, HOUR3, HOUR4, HOUR5, HOUR6, HOUR7)
) upvt

Related

How to display data by rolling day for three months when data has datestamp for state change by column

I have data formatted like this:
ID
Date Created
Date Ready to Start
Date In Progress
Date Dev Complete
Date Test Complete
Date Accepted
1
2021-11-01 12:01:15
2021-11-02 14:01:15
2021-11-04 05:01:15
2021-11-04 12:01:15
2021-11-05 12:01:15
2021-11-06 12:01:15
2
2021-11-01 12:01:45
NULL
2021-11-03 12:01:15
NULL
NULL
2021-11-05 12:01:15
3
2021-11-03 11:11:05
2021-11-04 12:01:15
NULL
NULL
NULL
NULL
4
2021-11-05 19:31:45
2021-11-05 12:01:15
2021-11-06 12:01:15
NULL
NULL
NULL
5
2021-11-04 13:21:25
NULL
NULL
NULL
NULL
NULL
and I need it formatted like this:
Date
Created
Ready to Start
In Progress
Dev Complete
Test Complete
Accepted
2021-11-01
2
0
0
0
0
0
2021-11-02
0
1
0
0
0
0
2021-11-03
1
0
1
0
0
0
2021-11-04
0
1
1
1
0
0
2021-11-05
1
1
0
0
1
1
2021-11-06
0
0
1
0
0
1
with rolling dates going back 3 months from current date.
I am unsure of how to start this... Would I union a recursive table creating the rolling calendar to the existing data or would I do a series of custom selects for counts and then group by date?
Try something like this:
DECLARE #Today date = GetDate();
DECLARE #MinDate date = DateAdd(month, -3, #Today);
DECLARE #DayCount int = 1 + DateDiff(day, #MinDate, #Today);
WITH cteNumbers As
(
/* Use a tally-table here if you have one: */
SELECT TOP (#DayCount)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) As N
FROM
sys.all_columns
),
cteCalendar As
(
SELECT
DateAdd(day, N, #MinDate) As [Date],
DateAdd(day, N + 1, #MinDate) As NextDay
FROM
cteNumbers
)
SELECT
C.[Date],
(
SELECT Count(1)
FROM YourTable As T
WHERE T.[Date Created] >= C.[Date]
And T.[Date Created] < C.NextDay
) As [Created],
(
SELECT Count(1)
FROM YourTable As T
WHERE T.[Date Ready to Start] >= C.[Date]
And T.[Date Ready to Start] < C.NextDay
) As [Ready to Start],
...
FROM
cteCalendar As C
;

How to get Count and Sum based on ID

I have a following table called lobby
QueueID FkBranch IsActive Status AddedLocalTime CompletedTime FkAssistTypeID
553279 16 1 5 7/12/2019 20:06 7/12/2019 21:10 2
553278 16 1 5 7/12/2019 20:07 7/12/2019 21:11 1
553277 16 1 5 7/12/2019 20:08 7/12/2019 21:10 1
553276 16 1 5 7/12/2019 20:09 7/12/2019 21:11 1
553275 16 1 5 7/13/2019 20:10 7/13/2019 21:10 2
553274 16 1 5 7/13/2019 20:11 7/13/2019 21:11 2
553278 17 1 5 7/14/2019 20:07 7/14/2019 21:11 1
553277 17 1 5 7/14/2019 20:08 7/14/2019 21:10 1
553276 18 1 5 7/14/2019 20:09 7/14/2019 21:11 2
553275 18 1 5 7/15/2019 20:10 7/15/2019 21:10 2
553274 18 1 5 7/15/2019 20:11 7/15/2019 21:11 2
And Branch table and Its data as follows
BranchID BranchName IsActive
16 Delhi 1
17 Karnataka 1
18 Telangana 1
Now I need to get a count of FkAssistTypeID of each location between AddedLocalTime and also need to take summation of the time difference of AddedLocalTime and CompletedTime.
I have a function to get the time Difference of two dates and it as follows
dbo.fnTimetoSeconds(AddedLocalTime, CompletedTime, NULL)
CREATE FUNCTION [dbo].[fnTimetoSeconds]
(
#dateOne DATETIME,#dateTwo DATETIME,#dateToConvert DATETIME
)
RETURNS INT
AS
BEGIN
DECLARE #date DATETIME
DECLARE #retValue INT
IF(#dateToConvert IS NULL)
BEGIN
SET #dateToConvert = CASE WHEN(#dateTwo>#dateOne) THEN #dateTwo-#dateOne ELSE #dateOne-#dateTwo END
END
SET #date = DATEADD(D, 0, DATEDIFF(D, 0, #dateToConvert))
IF(DATEPART(yy,#dateToConvert) = 1900)
BEGIN
SET #retValue = DATEDIFF(s,#date,#dateToConvert) + CASE WHEN DATEDIFF(D, 0, #dateToConvert) > 0 THEN DATEDIFF(D, 0, #dateToConvert) ELSE 0 END * 3600 * 24
END
ELSE
BEGIN
SET #retValue = DATEDIFF(s,#date,#dateToConvert)
END
RETURN #retValue
END
My expected output is,
* Please be noted, This Average column need to calculate, Suppose
when FkAssistTypeID = 1 and AddedLocalTime between 7/12/2019 and 7/14/2019 then by passing that row's AddedLocalTime and CompletedTime values fnTimetoSeconds taking time differance and take the summation of each time diffrences and divide it by count .
I need to add the above output to a temporary table. How can I do this?
I just tried this, but this is not the expected
select
b.BranchId AS ID,
b.BranchName,
count(case l.FkAssistTypeId when 1 then 1 end) as CountOf1,
SUM(CASE WHEN (l.FkAssistTypeId = 1) THEN COALESCE((dbo.fnTimetoSeconds(CompletedTime, AssistedTime, NULL)),0) ELSE 0 END) AS Average
from Branch b left join Lobby l
on b.BranchId = l.FkBranchId
where l.IsActive = 1 AND b.IsTestBranch = 0 AND CAST(l.AddedLocalTime as DATE) = '2019-07-12'
group by b.BranchId, b.BranchName
How about something like this :
SELECT Vals.ID,
Vals.BranchName ,
Vals.CountOf1,
CASE WHEN (Vals.CountOf1 = 0) THEN 0 ELSE Vals.mySum/Vals.CountOf1 END as AveSecs
INTO tmpTbl
FROM
(SELECT
b.BranchId AS ID,
b.BranchName,
count(l.FkAssistTypeI) as CountOf1,
SUM(DATEDIFF(second, CompletedTime, AssistedTime)) AS mySum
FROM Branch b
LEFT OUTER JOIN Lobby l
ON b.BranchId = l.FkBranchId
WHERE l.IsActive = 1
AND b.IsTestBranch = 0
AND l.FkAssistTypeI = 1
AND l.AddedLocalTime >= '2019/07/12'
AND l.AddedLocalTime < DATEADD(day, 1, '2019/07/15')
GROUP BY b.BranchId, b.BranchName) as Vals
The query first gets the count and the sum between 2019/07/12 00:00 and 2019/07/15 00:00 (not including). Then it inserts into a temp table (as you indicated) while calculating the average value. Note that you will need to test it and adjust it a bit since I have not run it. Also, I just used date diff to calculate the time in seconds, but you can use your function if needed. They should come up with the same value most times.
Not sure why you use the scalar function to get the date difference, even with the current logic, it's possible to do it in the query without the need of the function.
SELECT
b.BranchID
, b.BranchName
, ISNULL(l.CountOF1,0) CountOF1
, ISNULL(l.Average ,0) Average
FROM
#Branch b
LEFT JOIN (
SELECT
FkBranch
, IsActive
, COUNT(*) CountOF1
, AVG(DATEDIFF(SECOND, AddedLocalTime, CompletedTime)) Average
FROM #lobby l
WHERE
FkAssistTypeID = 1
AND AddedLocalTime BETWEEN '7/12/2019 00:00:00' AND '7/14/2019 23:59:59'
GROUP BY FkBranch, IsActive
) l ON l.FkBranch = b.BranchID AND l.IsActive = b.IsActive
WHERE
b.IsActive = 1

select data, grouped as Histogram

I have data in this format:
CREATE TABLE data(y int)
INSERT INTO data VALUES ((1))
INSERT INTO data VALUES ((55555))
INSERT INTO data VALUES ((55555))
INSERT INTO data VALUES ((99999))
I want to create a histogram, for to get a rough overview, of how my data is distributed. I am thinking of this format as output:
lowerBoundary upperBoundary y
------------- ------------- -----------
0 9999 1
10000 19999 0
20000 29999 0
30000 39999 0
40000 49999 0
50000 59999 2
60000 69999 0
70000 79999 0
80000 89999 0
90000 99999 1
You will have to create a table of numbers, so that the 0-rows will be displayed correctly. Then you can calculate lower and upper boundary of each "group".
Example SQL:
SELECT lowerBoundary, upperBoundary, COUNT(d.y) AS y
FROM (
SELECT n*10000 AS lowerBoundary, (n+1)*10000-1 AS upperBoundary
FROM (
-- Selects possible groups. Make this big enough for your data.
SELECT ones.n + 10*tens.n + 100*hundreds.n AS n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n)
) numbersTable
) boundaries
-- join with data
LEFT JOIN data d
ON d.y BETWEEN lowerBoundary AND upperBoundary
-- avoid trailing '0' rows
WHERE lowerBoundary <= (SELECT MAX(d.y) FROM data d)
GROUP BY lowerBoundary, upperBoundary
ORDER BY 1
Click here to run this skript at SQL-Fiddle
Another option...
I use a TVF to generate dynamic ranges. Being a single-statement function, it is extremely fast. Furthermore, if you can't use a UDF, the logic is easily ported into a cte or sub-query.
Select RetVal1
,RetVaL2
,y = sum(case when y is null then 0 else 1 end)
From [dbo].[udf-Range-Number-Span](0,100000,10000) A
Left Join Data B on y>=RetVal1 and y<RetVal2
Group By RetVal1,RetVal2
Returns
RetVal1 RetVaL2 y
0.00 10000.00 1
10000.00 20000.00 0
20000.00 30000.00 0
30000.00 40000.00 0
40000.00 50000.00 0
50000.00 60000.00 2
60000.00 70000.00 0
70000.00 80000.00 0
80000.00 90000.00 0
90000.00 100000.00 1
The UDF if needed
CREATE FUNCTION [dbo].[udf-Range-Number-Span] (#R1 money,#R2 money,#Incr money)
Returns Table
Return (
with cte0(M) As (Select cast((#R2-#R1)/#Incr as int)),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a,cte1 b,cte1 c,cte1 d,cte1 e,cte1 f,cte1 g,cte1 h )
Select RetSeq=1,RetVal1=#R1,RetVal2=#R1+#Incr
Union All
Select N+1,(N*#Incr)+#R1,((N*#Incr)+#R1)+#Incr
From cte2,cte0
Where N<cte0.M
)
--Max 100 million observations
--Select * from [dbo].[udf-Range-Number-Span](1,4,.5)

Distinct values with occurrences count

I want to count the number of times a vendor title occurs in a resultset, but if I use COUNT combined with GROUP BY, I only get either a 0 or 1 in the resultset
e.g. my results now look like this:
id vendortitle cnt
184 Hotel 1
198 A3 1
199 Las Vegas 1
200 Hotel-Restaurant 1
1252 Hansen 1
1253 Sunrise 1
1255 NULL 0
1256 Winsel 1
1257 Olde North 1
1258 A Castle 1
1259 A Castle 1
1262 Restaurant Rich 1
1263 NULL 0
1264 NULL 0
1265 NULL 0
1266 NULL 0
1269 My venue 1
1270 My venue 1
1271 My venue 1
1272 My venue 1
But I want this (I don't really actually need the NULL values):
id vendortitle cnt
184 Hotel 1
198 A3 1
199 Las Vegas 1
200 Hotel-Restaurant 1
1252 Hansen 1
1253 Sunrise 1
1255 NULL 5
1256 Winsel 1
1257 Olde North 1
1258 A Castle 2
1262 Restaurant Rich 1
1269 My venue 4
My SQL statement:
SELECT DISTINCT(vendortitle),id,COUNT(vendortitle) as cnt FROM (select ROW_NUMBER() OVER (ORDER BY vendortitle DESC) as RowNum,
id,vendortitle
FROM
(
SELECT
uf.id,coalesce(l.title, a.title) as vendortitle
FROM userfavorites uf
INNER JOIN vendor_photos vp ON uf.objectid = vp.id
LEFT JOIN homes l on vp.objecttype= 1 and vp.objectid = l.id
LEFT JOIN hotels a on vp.objecttype= 2 and vp.objectid = a.id
) as info
) as allinfo
WHERE RowNum > 0 AND RowNum <= 50
GROUP BY vendortitle,RowNum, id
There are a lot of things in your query that you don't need. The derived table isn't really needed, the DISTINCT and ROW_NUMBER either. This should do the work:
SELECT MIN(uf.id) id,
COALESCE(l.title, a.title) as vendortitle,
COUNT(*) as cnt
FROM userfavorites uf
INNER JOIN vendor_photos vp
ON uf.objectid = vp.id
LEFT JOIN homes l
ON vp.objecttype= 1
AND vp.objectid = l.id
LEFT JOIN hotels a
ON vp.objecttype = 2
AND vp.objectid = a.id
GROUP BY COALESCE(l.title, a.title);

SQL Server Left Outer Join returns either too many rows or incorrect rows

I have a SQL Server query that I want to do a left join to 2 tables. I need it to return all of the rows in the 1st table and only the matching data in the other 2 tables. My first incarnation created some sort of cross product with more than 10 times the required rows. So I added a rowcount function and had it filter for all rowcount values = 1. Now it returns the correct number of rows but with the wrong data from the other 2 tables.
I have certain filter rules (case statements) I apply to the contents of the 2 right tables to determine what, if anything, to return. I think this may be the source of the problem, but I'm not sure. I've tried using the filter code both within the select statement and as a where statement at the end of the query and even as part of the join on statement. I also tried using cross and outer applies. All to no avail.
I'm trying to achieve this without using loops. Is it possible? Please see the code below. This code generates too many rows. I won't repeat the 2nd iteration as it adds an extra select on top and a filter for m= 1 to the bottom to generate the correct number of rows.
These are the 2 tables that I am left joining to. Note that the 2nd table is only a small subset of a much larger table.
Thanks!!!
SELECT
GLENTRY.Fac + GLENTRY.Rundt + GLENTRY.Jrnllog AS UniqueID,
GLENTRY.Fac AS Fac,
(CASE WHEN
EntityTranslate2014.AcctEnd <> '' AND EntityTranslate2014.AcctEnd = SUBSTRING(GLENTRY.Acct,6,3)
THEN
EntityTranslate2014.Entity
ELSE
CASE WHEN
EntityTranslate2014.AcctEnd <> 0 AND EntityTranslate2014.Acct = GLENTRY.Acct
THEN
EntityTranslate2014.Entity
ELSE
CASE WHEN
EntityTranslate2014.SSDept <> 0 AND EntityTranslate2014.SSDept = SUBSTRING(GLENTRY.Acct,1,4)
THEN
EntityTranslate2014.Entity
ELSE
CASE WHEN
EntityTranslate2014.SSDept = 0
AND
EntityTranslate2014.AcctEnd = ''
AND
EntityTranslate2014.Acct = 0
THEN
EntityTranslate2014.Entity
ELSE
''
END
END
END
END)
AS NEWEntity,
(CASE WHEN AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0'
THEN
AcctTranslate2014.NEWDept
ELSE
Null
END)
AS Department,
(CONVERT(DATETIME,GLENTRY.Period + '/01/' +
CASE WHEN
CONVERT(NVARCHAR(4),GLENTRY.Yearz) = ''
THEN
'2014'
ELSE
GLENTRY.Yearz
END)
AS YEARPER,
GLENTRY.Acct) AS SSAcct,
CONVERT(NVARCHAR(20),
CASE WHEN
AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0'
THEN
AcctTranslate2014.NEWAcct
ELSE
Null
END)
AS NEWAccount,
GLENTRY.Rundt,
GLENTRY.Jrnlid,
GLENTRY.Amount,
GLENTRY.Dc,
GLENTRY.Ref,
GLENTRY.Refdt,
CONVERT(NVARCHAR(100),'Import of SS ' + CONVERT(VARCHAR(2),GLENTRY.Period) + '/' + CONVERT(VARCHAR(4),GLENTRY.Yearz) + ' GL activity') AS Descr,
GLENTRY.Invnr,
GLENTRY.Jrnllog,
ROW_NUMBER() OVER(PARTITION BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period, GLENTRY.Pagez, GLENTRY.Acct, GLENTRY.Rundt, GLENTRY.Jrnlid,
GLENTRY.Amount, GLENTRY.Dc, GLENTRY.Ref, GLENTRY.Refdt, GLENTRY.Descr, GLENTRY.Invnr, GLENTRY.Origfac, GLENTRY.Jrnllog, GLENTRY.Seq
ORDER BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period) AS m
FROM GLENTRY
LEFT OUTER JOIN
EntityTranslate2014 ON GLENTRY.Fac = EntityTranslate2014.Fac
LEFT OUTER JOIN
AcctTranslate2014 ON CONVERT(VARCHAR(8),GLENTRY.Acct) = AcctTranslate2014.SSAcct
WHERE GLENTRY.Yearz = 2014 AND GLENTRY.Period = 11
EntityTranslate2014 File
Fac Entity Descr AcctEnd SSDept Acct
1 51900 Entity1 0 0
2 50901 Entity2 0 0
3 10100 Entity3 0 0
3 10500 Entity4 4016 0
3 10500 Entity4 4020 0
3 10500 Entity4 4022 0
3 10500 Entity4 4024 0
3 10500 Entity4 4028 0
3 10500 Entity4 7016 0
4 30900 Entity5 0 0
5 10300 Entity6 0 0
6 11300 Entity7 0 0
7 11100 Entity8 0 0
7 11500 Entity9 4016 0
7 11500 Entity9 4020 0
7 11500 Entity9 4022 0
7 11500 Entity9 4024 0
7 11500 Entity9 4028 0
7 11500 Entity9 7016 0
9 32909 Entity10 0 0
10 12100 Entity11 0 0
11 32901 Entity12 0 0
12 53900 Entity13 0 0
13 10200 Entity14 0 0
14 32914 Entity15 0 0
15 32915 Entity16 0 0
16 11200 Entity17 0 0
17 32917 Entity18 0 0
18 32918 Entity19 0 0
19 32919 Entity20 0 0
20 32920 Entity21 0 0
21 13100 Entity22 0 0
22 52900 Entity23 0 0
89 99900 Entity24 0 0
123 12300 Entity25 0 0
124 12200 Entity26 0 0
133 13300 Entity27 0 0
201 11201 Entity28 0 0
202 11202 Entity29 0 0
402 25402 Entity30 0 0
403 25403 Entity31 0 0
549 25430 Entity32 0 0
549 25432 Entity33 7195 0
910 50910 Entity34 0 0
911 50911 Entity35 0 0
21 13500 Entity36 4016 0
21 13500 Entity36 4020 0
21 13500 Entity36 4022 0
21 13500 Entity36 4024 0
21 13500 Entity36 4028 0
21 13500 Entity36 7016 0
16 11202 Entity37 0 002 0
16 11201 Entity37 0 001 0
16 11200 Entity38 0 30918000
16 11200 Entity38 0 31918000
16 11200 Entity38 0 32110000
AcctTranslate2014
NewAcct SSAcct NewDEpt Fac
10111500 111200010 000 0
10111600 111200050 000 0
10111700 111550010 000 0
10113092 111050450 000 0
10115090 111050010 000 0
FROM GLENTRY
LEFT OUTER JOIN EntityTranslate2014
ON GLENTRY.Fac = EntityTranslate2014.Fac
LEFT OUTER JOIN AcctTranslate2014
ON CONVERT(VARCHAR(8), GLENTRY.Acct) = AcctTranslate2014.SSAcct
There's no relation defined between EntityTranslate2014 and AcctTranslate2014. If there's more than one record in either table for the corresponding record in GLENTRY, then those two tables will effectively cross-join with each other. For example, if a single GLENTRY joins to two records in EntityTranslate2014 and two records in AcctTranslate2014, then you'll get 4 records, one for each possible combination. That's simply how JOIN is defined.
If you know that this is happening and you know there's actually no relation, and you just want, say, for each record in GLENTRY the first record in EntityTranslate2014 to match to the first record in AcctTranslate2014, and the second record in EntityTranslate2014 to match to the second record in AcctTranslate2014 and so on, you can do what I've heard called a "ZIP JOIN":
FROM GLENTRY
LEFT OUTER JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Fac ORDER BY <SomeField>) row_order
FROM EntityTranslate2014) ET2014
ON GLENTRY.Fac = ET2014.Fac
LEFT OUTER JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY SSAcct ORDER BY <SomeField>) row_order
FROM AcctTranslate2014) AT2014
ON CONVERT(VARCHAR(8), GLENTRY.Acct) = AT2014.SSAcct
AND AT2014.row_order = ET2014.row_order
Obviously, don't use SELECT *; this is just an example. So, what this is doing is assigning some order to each OUTER table, and them matching them up with each other. Thus, each record in each OUTER table will only show up once. If there's a mis-match, then the records from the other table will appear as NULLs.
The other common alternative is to simply return the first record for each OUTER table, so that you're guaranteed to only have one GLENTRY record since each OUTER table only contributes one or zero records for each key field.
If that doesn't work for your purposes, then you'll probably need to use two queries, and will need to do the matching in your application.
You can use a Correlated Subquery to "loop" through each row in one table comparing it to rows in another table without actually writing loop logic (which should be avoided in SQL). The rows in the inner query are compared to each row in the outer query based on the where predicate. Correlated Subqueries also allow you to filter the WHERE clause by the TOP 1 if more than 1 record would be returned in the WHERE clause query (as in the code below). This is just a brief example, as I don't have time to write out the code in its entirety, but it may help.
SELECT Entity
FROM EntityTranslate2014 ent
WHERE ent.AcctEnd <> ''
AND AcctEnd = (SELECT TOP 1 SUBSTRING(glt.Acct,6,3)
FROM Glentry glt
WHERE glt.fac = ent.fac
AND glt.Year = 2014
AND glt.Period = 11
ORDER BY glt.Fac, glt.Yearz, glt.Period)
In this example, each record from the EntityTranslate2014 table is compared to the results of the inner query based on the predicate that Glentry.Fac = EntityTraslate2014.fac. Hope that's helpful.
For more information on Correlated Subqueries, check out the following link.
https://technet.microsoft.com/en-us/library/ms187638%28v=sql.105%29.aspx
Updated...
You can use ROW_NUMBER to count sequences in the joined results, and ORDER BY to sort them so that the row you want is first in that sequence.
Thank you for posting the loop construct, I think I was able to approximate it's function with a series of CTEs(WITH alias as (...)). I'm not sure that I got all your table/field names correct, but this should be close to functional. A major guess on my part was how the "first" entity should be determined, you may need to tune the order by in the row number functions in the CTEs.
WITH ENTFIND AS (
SELECT
EntityTranslate2014.Fac,
EntityTranslate2014.Acct,
EntityTranslate2014.AcctEnd,
EntityTranslate2014.Entity,
EntityTranslate2014.SSDept,
EntityTranslate2014.Entity,
ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E1Sort
FROM
EntityTranslate2014
),
E2 AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E2Sort
FROM
ENTFIND
WHERE
AcctEnd <> 0
),
E3 AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E3Sort
FROM
ENTFIND
WHERE
SSDept <> 0
),
E4 AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Fac, Acct, AcctEnd ORDER BY Fac, Acct, AcctEnd, Entity) as E4Sort
FROM
ENTFIND
WHERE
Acct = 0
AND
AcctEnd = ''
AND
SSDept = 0
)
SELECT
GLENTRY.Fac + GLENTRY.Rundt + GLENTRY.Jrnllog AS UniqueID,
GLENTRY.Fac AS Fac,,
E1.Entity,
E2.Entity,
E3.Entity,
E4.Entity,
COALESCE(E1.Entity,E2.Entity,E3.Entity,E4.Entity) as 1stFoundEntity,
(CASE WHEN
EntityTranslate2014.AcctEnd <> '' AND EntityTranslate2014.AcctEnd = SUBSTRING(GLENTRY.Acct,6,3)
THEN
EntityTranslate2014.Entity
ELSE
CASE WHEN
EntityTranslate2014.AcctEnd <> 0 AND EntityTranslate2014.Acct = GLENTRY.Acct
THEN
EntityTranslate2014.Entity
ELSE
CASE WHEN
EntityTranslate2014.SSDept <> 0 AND EntityTranslate2014.SSDept = SUBSTRING(GLENTRY.Acct,1,4)
THEN
EntityTranslate2014.Entity
ELSE
CASE WHEN
EntityTranslate2014.SSDept = 0
AND
EntityTranslate2014.AcctEnd = ''
AND
EntityTranslate2014.Acct = 0
THEN
EntityTranslate2014.Entity
ELSE
''
END
END
END
END)
AS NEWEntity,
(CASE WHEN AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0'
THEN
AcctTranslate2014.NEWDept
ELSE
Null
END)
AS Department,
(CONVERT(DATETIME,GLENTRY.Period + '/01/' +
CASE WHEN
CONVERT(NVARCHAR(4),GLENTRY.Yearz) = ''
THEN
'2014'
ELSE
GLENTRY.Yearz
END)
AS YEARPER,
GLENTRY.Acct) AS SSAcct,
CONVERT(NVARCHAR(20),
CASE WHEN
AcctTranslate2014.Fac = GLENTRY.Fac OR AcctTranslate2014.Fac = '0'
THEN
AcctTranslate2014.NEWAcct
ELSE
Null
END) AS NEWAccount,
GLENTRY.Rundt,
GLENTRY.Jrnlid,
GLENTRY.Amount,
GLENTRY.Dc,
GLENTRY.Ref,
GLENTRY.Refdt,
CONVERT(NVARCHAR(100),'Import of SS ' + CONVERT(VARCHAR(2),GLENTRY.Period) + '/' + CONVERT(VARCHAR(4),GLENTRY.Yearz) + ' GL activity') AS Descr,
GLENTRY.Invnr,
GLENTRY.Jrnllog,
ROW_NUMBER() OVER(PARTITION BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period, GLENTRY.Pagez, GLENTRY.Acct, GLENTRY.Rundt, GLENTRY.Jrnlid,
GLENTRY.Amount, GLENTRY.Dc, GLENTRY.Ref, GLENTRY.Refdt, GLENTRY.Descr, GLENTRY.Invnr, GLENTRY.Origfac, GLENTRY.Jrnllog, GLENTRY.Seq
ORDER BY GLENTRY.Fac, GLENTRY.Yearz, GLENTRY.Period) AS m
FROM
GLENTRY
LEFT JOIN
ENTFIND E1 ON GLENTRY.Fac = E1.Fac
AND SUBSTRING(GLENTRY.Acct,6,3) = E1.AcctEnd
AND E1Sort = 1
LEFT JOIN
E2 ON GLENTRY.Fac = E2.Fac
AND GLENTRY.Acct = E2.Acct
AND E2.E2Sort = 1
LEFT JOIN
E3 ON GLENTRY.Fac = E3.Fac
AND SUBSTRING(GLENTRY.Acct,1,4) = E3.SSDept
AND E3.E3Sort = 1
LEFT JOIN
E4 ON GLENTRY.Fac = E4.Fac
AND E4.E4Sort = 1
LEFT OUTER JOIN
EntityTranslate2014 ON GLENTRY.Fac = EntityTranslate2014.Fac
LEFT OUTER JOIN
AcctTranslate2014 ON CONVERT(VARCHAR(8),GLENTRY.Acct) = AcctTranslate2014.SSAcct
WHERE
GLENTRY.Yearz = 2014 AND GLENTRY.Period = 11
I want to thank everyone for their ideas while I tried to get my query to work. I ended up with following solution. Note, for the sake of brevity, I only included the code for determining the Entity field. It's not pretty and I'm sure it's optimal, but it works.
Thanks again for all of your help!!
CASE WHEN
(SELECT
BET.Entity
FROM
BET
WHERE
GL.Fac = BET.Fac
AND
BET.AcctEnd <> ''
AND
BET.AcctEnd = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),6,3))
IS NOT Null
THEN
(SELECT
BET.Entity
FROM
SOSViews.dbo.BI360EntityTranslate2014 BET
WHERE
GL.Fac = BET.Fac AND BET.AcctEnd <> ''
AND
BET.AcctEnd = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),6,3))
ELSE
CASE WHEN
(SELECT
BET.Entity
FROM
SOSViews.dbo.BI360EntityTranslate2014 BET
WHERE
GL.Fac = BET.Fac
AND
BET.SOSDept <> 0
AND
BET.SOSDept = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),1,4))
IS NOT Null
THEN
(SELECT
BET.Entity
FROM
BET
WHERE
GL.Fac = BET.Fac
AND
BET.SOSDept <> 0
AND
BET.SOSDept = SUBSTRING(CONVERT(NVARCHAR(8),GL.Acct),1,4))
ELSE
CASE WHEN
(SELECT
BET.Entity
FROM
BET
WHERE
GL.Fac = BET.Fac
AND
BET.SOSDept = 0 AND BET.AcctEnd = '' AND BET.Acct = 0)
IS NOT Null
THEN
(SELECT
BET.Entity
FROM
BET
WHERE
GL.Fac = BET.Fac
AND
BET.SOSDept = 0 AND BET.AcctEnd = '' AND BET.Acct = 0)
ELSE
'No Entity Translation'
END
END
END)
AS Entity,
Note: Although it may not be directly applicable to the OP's question, sometimes if there are too many rows in a join, it is as simple as joining on the wrong column. This just happened to me....

Resources