FULL JOIN doesn't work in two ways - sql

I'm trying to write a FULL JOIN statement in SQL but it doesn't work as fine as I would like.
Here is my full statement
SELECT
t.tacNom, bh.heuresChiffrees,
SUM(ISNULL(heures,0)) + SUM(ISNULL(minutes,0)/60) as HeuresRealisee, (ISNULL(bh.heuresChiffrees,0) - (SUM(ISNULL(heures,0)) + (SUM(ISNULL(minutes,0))/60))) as Solde,
(SUM(DISTINCT minutes)%60) as Soldeminutes
FROM tbl_BalanceHeures bh
FULL OUTER JOIN tbl_HeuresTimbrage_new ht ON ht.idProjet = bh.proIdProjetExterne
AND ht.idxTache = bh.idxTache
AND ht.idxDep= bh.idxDepartement
INNER JOIN tbl_Taches t on t.idTache = bh.idxTache
WHERE proIdProjetExterne = '00Z 000104' AND bh.idxDepartement = 184
GROUP BY t.tacNom ,bh.heuresChiffrees
Here are the result of the statement
idTache heuresChiffrees HeuresRealisee Solde
332 25 0 25
330 50 0 50
327 100 42 58
331 100 23 77
Here are the records in my table tbl_Balance and the statement
select t.idTache, bh.heuresChiffrees
from tbl_BalanceHeures bh
INNER JOIN tbl_Taches t on t.idTache = bh.idxTache
WHERE proIdProjetExterne= '00Z 000104' AND bh.idxDepartement = 184
idTache heuresChiffrees
330 50
331 100
327 100
332 25
Here are the records in tbl_HeuresTimbrage and the statement
Select t.idTache, ht.heures as heuresRealisees
From tbl_HeuresTimbrage_new ht
INNER JOIN tbl_Taches t on t.idTache = ht.idxTache
WHERE idProjet= '00Z 000104' AND ht.idxDep = 184
idTache heuresRealisees
327 32
331 23
327 10
334 4
So, In my FULL JOIN Statement I am expected to find
IdTache heuresChiffrees Heures Realisees
327 100 10
330 50 NULL (or 0)
331 100 23
332 25 NULL (or 0)
334 NULL(or 0) 4
I've tried to put my WHERE clause in the Full JOIN but it doesn't work

It's most likely because of your WHERE clause. It is acting on the joined result, and so if the A side is null, it will eliminate those rows.
You can change it to be a condition of the join instead, as so:
FROM A
FULL OUTER JOIN B
ON B.idProjet = A.proIdProjetExterne
AND B.idxTache = A.idxTache
AND B.idxDep= A.idxDepartement
AND A.proIdProjetExterne = 'xxx'
AND A.idxDepartement = 184
However, without seeing your actual data or schema, this is a bit of a guess...

Related

Is there is way to get SUM() of column without GROUPING by joining multiple tables in SQL Server

I am getting SUM() of amount in CrowdfundedUser table by GROUP BY CrowdfundID but difficult to get SUM() because all columns are unique.
Crowdfund:
CrowdfundID
GoalAmount
StartedDate
9
10000
09/02/2022
5
20000
10/02/2022
55
350000
11/02/2022
444
541256
12/02/2022
54
78458
13/02/2022
CrowdfundedUser:
ID
User ID
CrowdfundID
Amount
744
12214
9
1000
745
4124
5
8422
746
12214
55
784
747
12214
444
874
748
64554
54
652
CrowdfundiPaymentTransaction:
CrowdfundedUserID
Invoice
Amount
PaymentDate
744
RA45A14124
1000
09/02/2022
745
RA45A12412
8422
10/02/2022
746
RA45U14789
784
11/02/2022
747
RA45F12457
874
12/02/2022
748
RA45M00124
652
13/02/2022
My query :
SELECT
c.CrowdfundID,
SUM(cu.Amount),
SUM(cpt..Amount)
FROM
Crowdfund c
INNER JOIN
CrowdfundedUser cu ON c.CrowdfundID = cu.CrowdfundID
INNER JOIN
CrowdfundiPaymentTransaction cpt ON cu.ID = cpt.CrowdfundedUserID
GROUP BY
c.CrowdfundID
SELECT c.CrowdfundID,
SUM(cu.Amount) OVER (
ORDER BY c.CrowdfundID) Amount,
SUM(cpt..Amount) OVER (
ORDER BY c.CrowdfundID) CptAmount
FROM Crowdfund c
INNER JOIN CrowdfundedUser cu ON c.CrowdfundID = cu.CrowdfundID
INNER JOIN CrowdfundiPaymentTransaction cpt ON cu.ID = cpt.CrowdfundedUserID

Left outer join with condition sql

would anybody know if there is way to do conditional left outer join? I have ledger tables with actuals and reference department table from which I want to assign BMDIV.
But department reference table contains LC number or = 'xx'
What I want to do is to join ledger table with department table on LCTRYNUM, LC and DEPTNUM but in the case of LC = '**' join would be only LCTRYNUM and DEPTNUM.
Ledger acutal table
LCTRYNUM
LC
DEPTNUM
Amount
618
40
30813
100
618
50
30813
200
618
60
30813
300
618
10
30813
100
Business Division reference table
LCTRYNUM
LC
DEPTNUM
BMDIV
618
**
30813
30
618
10
30813
2P
Expected result would be this
LCTRYNUM
LC
DEPTNUM
Amount
BMDIV
618
40
30813
100
30
618
50
30813
200
30
618
60
30813
300
30
618
10
30813
100
2P
You can use two left joins, the first to bring in direct matches and the second for the default match:
select a.*, coalesce(d.bmdiv, d_default.bmdiv) as bmdiv
from actual a left join
divisions d
on a.lc = d.lc left join
divisions d_default
on d_default.lc = '**'

Using multiple and interdepended CROSS-APPLY across multiple tables

How can I use either CROSS APPLY (or INNER JOIN) to get data from one table based on the values in other tables?
I.e. I have the following tables:
Table Descriptions:
ProdID
Description
TrackNum
361
Test 1
499
388
Test 2
003
004
5599
238
Test 3
499
361
Test 10
555
004
Test 40
555
Table Products:
ProdID
ProductName
Price
361
P1
5.00
388
P2
5.00
004
P3
12.00
238
P4
6.00
515
P5
7.00
636
P6
7.00
775
P7
7.00
858
P8
8.00
Table Invoices:
ProdID
TrackNum
InvoiceID
361
499
718
388
199
718
004
499
718
238
499
718
361
555
333
004
555
444
361
111
444
388
222
333
616
116
565
717
116
565
361
003
221
388
003
221
004
5599
728
What I need my query to do is to:
Go into Invoices table first, and get only records that matches specified InvoiceID and TrackNum;
Then go into Products table and get only rows that have matches on ProdID between the data I pulled out in Step #1 and the data existis in the Products table.
Then finally get all columns from the Descriptions table, but only for the rows which I got in the Step #2 and which matches on ProdID.
What I need to have at the end is something like this (if I get more columns that is fine, but I do not want to get more rows):
ProdID
Description
TrackNum
361
Test 1
499
004
5599
238
Test 3
499
I have following query (and I have tried using INNER JOIN and CROSS APPLY) - but it returns me more rows than I need:
SELECT * FROM [Descriptions] AS [DES]
CROSS APPLY
(
select * from [Invoices] AS [INV] where [INV].[TrackNum] = '499' AND [INV].[InvoiceID] = '718'
) [INV]
CROSS APPLY
(
select * from [Products] AS [GP]
WHERE [GP].[ProdID] = [INV].[ProdID]
) [GP2]
WHERE
[DES].[ProdID] = [GP2].[ProdID]
order by [DES].[ProdID] asc
SELECT
*
FROM
invoices AS i
LEFT JOIN
descriptions AS d
ON d.prodid = i.prodid
AND d.tracknum = i.tracknum -- you don't have this, but I think it's required.
LEFT JOIN
products AS p
ON p.prodid = i.prodid
WHERE
i.invoiceid = 718
AND i.tracknum = 499
ORDER BY
i.prodid
One thing that concerns me is that both the invoices and descriptions have a column named tracknum, but your query and expected data indicate that you don't want to include that in the join? That's very confusing and either a poor column name, or a mistake in your query and example results.
Based on what you describe you want the following, start with your Invoices table and a where clause to get the right rows, then join on Products and Descriptions.
I'm also guessing that you want to match the Description on TrackNum? Since it appears you have a unique Description per ProdId/TrackNum combination.
select [INV].[ProdID], [DES].[Description], [INV].[TrackNum]
from [Invoices] as [INV]
inner join [Products] as [GP] on [GP].[ProdID] = [INV].[ProdID]
inner join [Descriptions] on [DES].[ProdID] = [GP].[ProdID] and [DES].[TrackNum] = [INV].[TrackNum]
where [INV].[TrackNum] = '499' AND [INV].[InvoiceID] = '718'
order by [DES].[ProdID] asc;
Note: You normally only use a 'CROSS APPLY' for queries where you want to run/evaluate something per row in your main table.
In this case the Inner Join is sufficient. You don't need to use Cross Apply

SQL Multiple WHERE Clauses against one table, with CASE statements

I wrote this query and it seems to be working to gather correct results, however, it also takes a VERY long time. I'm just wondering if there is a way to make it more efficient?
(I understand that it's inefficient because it is creating data tables and joining them back together - I just don't know how to get around it, specifically with the CASE issue included).
I'm working in Excel with an ODBC connection to AS400. The question marks allow user-entered parameters within Excel cells.
with W as
(
select yr as YEAR, pd as PERIOD, sum(amt1 + amt2 + amt3 + amt4) as SALES
from TABLE
group by yr, pd
order by yr desc, pd desc
), X as
(
select yr as YEAR, pd as PERIOD, sum(amt1 + amt2 + amt3 + amt4) as BSALES
from TABLE
where type = 'B'
group by yr, pd
order by yr desc, pd desc
), Y as
(
select
CASE WHEN pd = 1 THEN yr - 1 ELSE yr END as YEAR,
CASE WHEN pd = 1 THEN 12 ELSE pd - 1 END as PERIOD,
SUM(OM) as MODOM
from TABLE
group by yr, pd
order by yr desc, pd desc
), Z as
(
select
CASE WHEN pd = 1 THEN yr - 1 ELSE yr END as YEAR,
CASE WHEN pd = 1 THEN 12 ELSE pd - 1 END as PERIOD,
SUM(BOM) as BMODOM
from TABLE
where type = 'B'
group by yr, pd
order by yr desc, pd desc
)
select w.YEAR, w.PERIOD, w.SALES, x.BSales, y.MODOM, z.BMODOM
from w inner join x
on w.YEAR = x.YEAR and w.PERIOD = x.PERIOD
inner join y
on w.YEAR = y.YEAR and w.PERIOD = y.PERIOD
inner join z
on w.YEAR = z.YEAR and w.PERIOD = z.PERIOD
where w.YEAR between ? and ? and w.PERIOD between ? and ?
order by YEAR desc, PERIOD desc
I had to change the code slightly for privacy purposes, but I believe this all relays correctly.
Example Data:
Yr Pd Type Amt OM
18 2 A 45 181
18 2 B 33 163
18 2 A 40 153
18 1 B 39 136
18 1 B 24 142
18 1 B 53 143
18 1 A 41 186
18 1 A 78 197
17 12 A 98 139
17 12 A 54 159
17 12 B 78 181
17 12 B 45 101
17 11 A 28 134
17 11 A 77 192
17 11 A 75 110
17 11 B 60 135
17 11 B 83 170
17 10 B 72 114
17 10 A 26 118
17 10 A 95 111
17 9 A 12 112
17 9 B 14 171
Example Results
Yr Pd Sales Bsales MODOM BMODOM
18 2 118 33 804 421
18 1 235 116 580 282
17 12 275 123 741 305
17 11 323 143 343 114
17 10 193 72 283 171
Note that I need SALES at a TOTAL Level, and then at a TYPE B level. I also need OM at a TOTAL Level, and then also a Type B level - HOWEVER - I need to offset by one period. So the MODOM is for 17-10 is reflection of the OM total for period 17-9 in the table. (I hope that make sense).
EDIT I have this backward. The MODOM for 17-10 would actually reflect the OM value for 17-11, not the other way. Corrected EXPECTED RESULTS.
Yr Pd Sales Bsales MODOM BMODOM
18 2 118 33 0 0
18 1 235 116 497 163
17 12 275 123 804 421
17 11 323 143 580 282
17 10 193 72 741 305
So using Conditional aggregation you can at least get rid of 2 of your common table expressions which will help. Not ordering until your final presentation query instead of in each common table expression will help too. Using an actual date e.g. First of First Month of a Period could help eliminate what the potentially costly case expressions when defining previous period.
There are a few ways of writing this but this will give you an example of conditional aggregation.
And note the LEFT JOIN rather than INNER because your 1st period will always drop off your query if you use INNER
WITH PeriodSales AS(
SELECT
yr as YEAR
,pd AS PERIOD
,SUM(amt) as SALES
,SUM(CASE WHEN type = 'B' THEN amt END) as BSALES
FROM
Table
GROUP BY
yr
,pd
)
, PreviousPeriod AS
(
SELECT
CASE WHEN pd = 12 THEN yr + 1 ELSE yr END as YEAR
,CASE WHEN pd = 12 THEN 1 ELSE pd + 1 END as PERIOD
,SUM(OM) as MODOM
,SUM(CASE WHEN type = 'B' THEN OM END) as BMODOM
FROM
Table
GROUP BY
CASE WHEN pd = 12 THEN yr + 1 ELSE yr END
,CASE WHEN pd = 12 THEN 1 ELSE pd + 1 END
)
SELECT
ps.YEAR
,ps.PERIOD
,ps.SALES
,ps.BSALES
,pp.MODOM
,pp.BMODOM
FROM
PeriodSales ps
LEFT JOIN PreviousPeriod pp
ON ps.YEAR = pp.YEAR
AND ps.PERIOD = pp.PERIOD
ORDER BY
ps.YEAR DESC
,ps.PERIOD DESC
Per your edits, to align to the "Previous Period" to get to the OM amounts you want you will actually want to ADD a period not subtract one in the example I used. I have tested this and it does work. There can be many other factors for performance that we cannot discover without knowing more about the tables and execution plans etc.

Microsoft access Query rolling total every 5 lines not using dates

I am new to Microsoft access.
I need a query that will allow me to sum a rolling total for every 5 lines of data. So on the sixth day I need a line to drop off the total and the new line to be added.
Fields:
ID, Daily_SUM
The results should be
ID Daily sum Weekly Sum
1 12
2 41
3 46
4 125
5 120 344
6 42 374
7 41 374
8 57 385
9 207 467
10 215 562
11 187 707
12 -43 623
13 45 611
14 56 460
15 40 285
16 8 106
17 95 244
18 580 779
19 360 1083
20 337 1380
You can do this with a correlated subquery. The challenge is actually getting NULL values on the first few rows:
select t.id, t.daily,
(select iif(count(*) = 7, sum(t3.daily), NULL)
from (select top 7 t2.daily
from table t2
where t2.id <= t.id
order by t2.id desc
) t3
) as weekly
from table t;
EDIT:
If we assume that the ids are assigned sequentially with no gaps, then you can use an explicit join:
select t.id, t.daily,
iif(count(*) = 7, sum(t2.daily), NULL) as weekly
from table t inner join
table t2
on t2.id between t.id - 6 and t.id
group by t.id, t.daily;