TSQL: Convert old where clause to Join syntax - tsql

I have this query that I want to put into the 'modern Join syntax'
SELECT
t.acct_order,
c1.acct_desc,
c1.position,
c1.end_pos,
Budget = sum(((g.data_set-1)/2)*(g.debits - g.credits)),
Actual = sum(((g.data_set-3)/-2)*(g.debits - g.credits))
FROM
glm_chart c1,
glm_chart c2,
glh_deptsum g,
#TEMPTABLE t
WHERE
g.acct_uno=c2.acct_uno
and c1.acct_code = t.acct_code
and c2.position between c1.position and c1.end_pos
and g.debits-g.credits<>0
and c1.book=1
and g.data_set in (1, 3)
and (g.period between 201201 and 201212)
GROUP by t.acct_order,
c1.acct_desc,
c1.position,
c1.end_pos
ORDER by t.acct_order
This is what I got to, but as you can see I can't seem to identify the join to table glh_deptsum (g) to C1 or to T
SELECT
t.acct_order,
c1.acct_desc,
c1.position,
c1.end_pos,
sum(((g.data_set-1)/2)*(g.debits - g.credits)) AS Budget,
sum(((g.data_set-3)/-2)*(g.debits - g.credits)) AS Actual,
FROM #TEMPTABLE T
INNER JOIN glm_chart c1 ON c1.acct_code = t.acct_code
INNER JOIN glh_deptsum g <---- HELP WHAT GOES HERE ---------
INNER JOIN glm_chart c2 ON c2.position between c1.position and c1.end_pos AND g.acct_uno=c2.acct_uno
WHERE
g.debits - g.credits <> 0
AND c1.book=1
AND g.data_set in (1, 3)
AND (g.period between 201201 and 201212)
GROUP BY t.acct_order,
c1.acct_desc, c1.position,c1.end_pos
ORDER BY t.acct_order
Can anyone let me know what I'm doing wrong please?

It looks like it should be this:
SELECT t.acct_order,
c1.acct_desc,
c1.position,
c1.end_pos,
Budget = sum(((g.data_set-1)/2)*(g.debits - g.credits)),
Actual = sum(((g.data_set-3)/-2)*(g.debits - g.credits))
FROM #TEMPTABLE t
INNER JOIN glm_chart c1
ON t.acct_code = c1.acct_code
INNER JOIN glm_chart c2
ON c2.position between c1.position and c1.end_pos
INNER JOIN glh_deptsum g
ON c2.acct_uno = g.acct_uno
WHERE g.debits-g.credits<>0
and c1.book=1
and g.data_set in (1, 3)
and (g.period between 201201 and 201212)
GROUP by t.acct_order,
c1.acct_desc,
c1.position,
c1.end_pos
ORDER by t.acct_order

Related

Displaying related child records on a single row

in DBs I'm familiar with, a query listing parents and children would look like this:
Parfirst ParLast Childfirst
Mary Smith Sally
Mary Smith Jim
Mary Smith Kim
However, I've been asked to create a report that looks like this:
Parfirst ParLast Child1 Child2 Child3 Child4
Mary Smith Sally Jim Kim
I'm at a loss as to how to accomplish this. Any suggestions are welcome.
You can do this using STRING_AGG function (if you are using SQL Server 2017 or newer) or to do some ugly XML magic using FOR XML:
SELECT t.ParFirst, t.ParLast, LEFT(t.Children, Len(t.Children)-1) As Children
FROM
(
SELECT DISTINCT p.ParFirst, p.ParLast,
(
SELECT c.ChildFirst + ',' AS [text()]
FROM dbo.Children c
WHERE c.ParentId = p.ParentId
ORDER BY c.ChildFirst
FOR XML PATH ('')
) l
FROM dbo.Parents p
) t
SELECT p.ParFirst, p.ParLast, STRING_ACC(c.ChildFirst, ',') as Children
FROM dbo.Parents p
LEFT JOIN dbo.Children c on c.ParentId = p.ParentId
GROUP BY p.ParFirst, p.ParLast
If you want to include every child name in it's own column, you must define these columns in your query and you won't be able to return any additional child, if there is more than the number of columns you defined. Use ROW_NUMBER like this:
select p.ParFirst, p.ParLast, c1.ChildFirst, c2.ChildFirst, c3.ChildFirst
from dbo.Parents p
outer apply ( select ChildFirst from (select ChildFirst, row_number() over (order by ChildFirst) as rowNo from dbo.Children c where c.ParentId = p.ParentId) t1 where t1.rowNo = 1 ) c1
outer apply ( select ChildFirst from (select ChildFirst, row_number() over(order by ChildFirst) as rowNo from dbo.Children c where c.ParentId = p.ParentId) t2 where t2.rowNo = 2 ) c2
outer apply ( select ChildFirst from (select ChildFirst, row_number() over(order by ChildFirst) as rowNo from dbo.Children c where c.ParentId = p.ParentId) t3 where t3.rowNo = 3 ) c3

Faster left join with last non-empty

Table1:
Shop
Manager
Date
Table2:
Shop
Date
Sales
I need to get Table2 with Manager field from Table1. I did the following trick:
select
t1.[Shop]
,t1.[Date]
,t1.[Sum]
,t2.[Manager]
from t1
left join t2
on t1.[Shop] = t2.[Shop]
and t2.[Date] = (select max(t2.[Date]) from t2
where t2.[Shop] = t1.[Shop]
and t2.[Date] < t1.[Date])
It works, but subquerying is very slow, so I wonder if there is more elegant and fast way to do so?
Some sample data to play around: http://pastebin.com/uLN6x5JE
may seem like a round about way but join on a single condition is typically faster
select t12.[Shop], t12.[Date], t12.[Sum]
, t12.[Manager]
from
( select t1.[Shop], t1.[Date], t1.[Sum]
, t2.[Manager]
, row_number() over (partition by t2.[Shop] order by t2.[Date] desc) as rn
from t1
join t2
on t2.[Shop] = t1.[Shop]
and t1.[Date] < t1.[Date]
) as t12
where t12.rn = 1
union
select t1.[Shop], t1.[Date], t1.[Sum]
, null as [Manager]
from t1
left join t2
on t2.[Shop] = t1.[Shop]
and t1.[Date] < t1.[Date]
group by t1.[Shop], t1.[Date], t1.[Sum]
having count(*) = 1
You may get much better performance by adding a covering index on t2 if you don't already have one:
create index T2ShopDate on t2 ([Shop], [Date]) include ([Manager])
Here is a version that uses a CTE to find all maximum manager dates first and then join back to t2 to get the manager:
;with MaxDates ([Shop], [Date], [Sum], [MaxMgrDate]) as
(
select
t1.[Shop]
,t1.[Date]
,t1.[Sum]
,max(t2.[Date])
from t1
left join t2
on t2.[Shop] = t1.[Shop]
and t2.[Date] < t1.[Date]
group by
t1.[Shop]
,t1.[Date]
,t1.[Sum]
)
select
MaxDates.[Shop]
,MaxDates.[Date]
,MaxDates.[Sum]
,t2.[Manager]
from MaxDates
inner join t2
on t2.[Date] = MaxDates.[MaxMgrDate]
You might be able to remove the second join back to t2 by using row_number():
;with MaxDates ([Shop], [Date], [Sum], [Manager], [RowNum]) as
(
select
t1.[Shop]
,t1.[Date]
,t1.[Sum]
,t2.[Manager]
,row_number() over (partition by (t1.[Shop]) order by t2.[Date] desc)
from t1
left join t2
on t2.[Shop] = t1.[Shop]
and t2.[Date] < t1.[Date]
)
select *
from MaxDates
where RowNum = 1

SQL insert into using CTE

I am facing a performance issue due to "Insert into" statement in sql. I am using a CTE to select data from multiple tables and insert into other table. It was working just fine until yesterday. Select takes less than a minute to retrieve the data where as insert into taking forever. Can some one please help me in understanding what i am doing wrong. Any help is highly appreciated. Thanks.
Here is my code:
I am using this query in an SP. I am trying to load 220K records to 1.5M records table.
;with CTE_A
AS
(
SELECT A1, A2,...
FROM dbo.A with (nolock)
WHERE A1 = <some condition>
GROUP BY a.A1,a.A2 , a.A3
), CTE_C as
(
SELECT C1, C2,....
FROM dbo.B with (nolock)
WHERE a.C1 = <some condition>
GROUP BY a.c1,a.C2 , a.C3
)
INSERT INTO [dbo].MainTable
SELECT
A1, A2, A3 , C1, C2, C3
FROM
CTE_A ta with (nolock)
LEFT OUTER JOIN
CTE_C tc with (nolock) ON ta.a1 = tc.a1 and ta.b1 = tc.b1 and ta.c1 = tc.c1
LEFT OUTER JOIN
othertable bs with (nolock) ON usd_bs.c = s.c
AND (A1 BETWEEN bs.a1 AND bs.a1)
AND bs.c1 = 1
try this method (temp table instead cte), perfomance must be much higher for your task
IF OBJECT_ID('Tempdb..#CTE_A') IS NOT NULL
DROP TABLE #CTE_A
IF OBJECT_ID('Tempdb..#CTE_C') IS NOT NULL
DROP TABLE #CTE_C
-------------------------------------------------------------
SELECT A1 ,
A2 ,...
INTO #CTE_A --data set into temp table
FROM dbo.A WITH ( NOLOCK )
WHERE A1 = <some condition>
GROUP BY a.A1 ,
a.A2 ,
a.A3
-------------------------------------------------------------
SELECT C1 ,
C2 ,....
FROM dbo.B WITH ( NOLOCK )
INTO #CTE_C --data set into temp table
WHERE a.C1 = <some condition>
GROUP BY a.c1 ,
a.C2 ,
a.C3
INSERT INTO [dbo].MainTable
SELECT A1 ,
A2 ,
A3 ,
C1 ,
C2 ,
C3
FROM #CTE_A AS ta
LEFT JOIN #CTE_C AS tc ON ta.a1 = tc.a1
AND ta.b1 = tc.b1
AND ta.c1 = tc.c1
LEFT JOIN othertable AS bs ON usd_bs.c = s.c
AND ( A1 BETWEEN bs.a1 AND bs.a1 )
AND bs.c1 = 1

T-SQL Outer JOIN like oracle syntax (+)

I've to join 3 tables.
select u.*, c1.name, c2.name
users u, country c1, country c2
where u.country_id = c1.country_id(+)
AND u.bank_country_id = c2.country_id(+)
The symbol (+) in oracle means that I want a Left Outer Join on both that table (c1 and c2)
How to do the same in T-SQL?
select u.*, c1.name, c2.name
from users u
left outer join country c1 on u.country_id = c1.country_id
left outer join country c2 on u.bank_country_id = c2.country_id

Aggregate similar row

Suppose I've a table like this:
NAME REF1 REF2 DRCT
A (null) Ra D1
A Rb (null) D1
A (null) Rc D2
B Rd (null) D3
B (null) Re D3
I want aggregate this table in something like:
NAME REF1 REF2 DRCT
A Rb Ra D1
A (null) Rc D2
B Rd Re D3
As you can see, i want aggregate each row with same name. I've search through COALESCE and various aggregate functions but I haven't found what i was looking for. Any idea?
Assuming that what I ask in my previous comment is true, (only null or a given value for REF1 and REF2 for each NAME, DRCT pair), this seems to work:
select NAME, M_REF1, M_REF2, DRCT
from (
select A.NAME, coalesce(A.REF1, B.REF1) m_REF1,
coalesce(A.REF2, B.REF2) m_REF2, A.REF1 A_REF1, B.REF1 B_REF1,
A.REF2 A_REF2, B.REF2 B_REF2, A.DRCT
from Table1 A JOIN Table1 B on A.NAME = B.NAME AND A.DRCT = B.DRCT)
WHERE A_REF1 = m_REF1 AND B_REF2 = m_REF2
UNION
select A.NAME, A.REF1, A.REF2, A.DRCT
FROM Table1 A JOIN
(select NAME, DRCT, COUNT(*)
from Table1
group by NAME, DRCT
HAVING COUNT(*) = 1) B ON A.NAME = B.NAME AND A.DRCT = B.DRCT;
The union is used because the rows with only one record are not included in the first SELECT.
But this is somewhat simpler, and works too:
select A.NAME, coalesce(A.REF1, B.REF1) M_REF1, coalesce(A.REF2,B.REF2) M_REF2,A.DRCT
from Table1 A LEFT OUTER JOIN Table1 B ON A.DRCT = B.DRCT AND A.NAME = B.NAME
WHERE NVL2(A.REF1,0,1) = 1 AND NVL2(B.REF1,0,1) =0
AND NVL2(A.REF2,0,1) = 0 AND NVL2(B.REF2,0,1) = 1
UNION
select A.NAME, A.REF1, A.REF2, A.DRCT
FROM Table1 A JOIN
(select NAME, DRCT, COUNT(*)
from Table1
group by NAME, DRCT
HAVING COUNT(*) = 1) B ON A.NAME = B.NAME AND A.DRCT = B.DRCT;