SQL join two table together - sql

I have the following tables:
table1
a b
1 100
2 200
3 300
4 400
table2
c b
55 100
55 200
56 300
I want to get the following output:
55 100 1
55 200 2
55 300 -
55 400 -
56 100 -
56 200 -
56 300 3
56 400 -
I tried the following:
SELECT *
FROM table1
full JOIN table2
output:
a b c a
1 100 55 100
1 100 55 200
1 100 55 100
1 100 55 200
2 300 56 300
....
also I tried:
SELECT *
FROM table1
join table2 on table1.b = table2.b
union
SELECT *
FROM table2
join table1 on table1.b = table2.b
the output:
1 100 55 100
1 200 55 200
3 300 56 300
Is this possible in microsoft SQL 2012? and how

I'm not completely sure I understand your expected outcome, but it sounds like you're looking for a FULL OUTER JOIN.
SELECT table1.a, COALESCE(table1.b, table2.b), table2.c
FROM table1
FULL OUTER JOIN table2 ON table1.b = table2.b
This will get the fields from table1 and, if any exist, map them to those from table2.
Given your example, it will return the following table.
A B C
1 100 55
2 200 55
3 300 56
4 400 (null)
I know that isn't the same as the expected result you gave, but this will correlate the data that actually exists.
I'm requesting clarification in a comment and will revise this as necessary.

Related

Conditional sum in SQL (SAS) (SUMIFS equivalent) - Part 2

Let say I have to table:
Table1:
ID Item
1 A
1 B
1 A
2 B
2 B
3 A
3 B
3 B
3 A
Table2:
ID A B C
1 91 94 90
2 100 97 93
1 97 94 96
2 97 95 90
3 99 100 93
1 90 97 97
Now I would like to take the sum conditional for my table1 from table2 (when the ID by row and the Item match by COLUMN):
ID Item Want
1 A 278
1 B 285
2 A 197
2 B
2 B
3 A
3 B
3 B
3 A
So that I have 278 is the sum of all item 1 in column A, 285 is the sum of all itme 1 in column B, 197 is the sum of all item 2 in column A.
So what am I supposed to do in SQL?
Thanks in advance.
You can use join and conditional aggregation:
select t1.id, t1.item,
sum(case when t1.item = 'A' then t2.A
when t1.item = 'B' then t2.B
when t1.item = 'C' then t2.C
end) as want
from table1 t1 left join
table2 t2
on t1.id = t2.id
group by t1.id, t1.item
Proc MEANS is built from the ground up for the sole purpose of computing statistics for aggregates.
Consider this example:
data have; input
ID $ A B C; datalines;
1 91 94 90
2 100 97 93
1 97 94 96
2 97 95 90
3 99 100 93
1 90 97 97
;
ods _all_ close;
proc means data=have stackodsoutput sum;
class id;
var a b c;
ods output summary=want;
run;
that produces data set

Properly 'Joining' two Cross Applies

I've got a query with three Cross-Applies that gather data from three different tables. The first Cr-Ap assists the 2nd and 3rd Cr-Ap's. It finds the most recent ID of a certain refill for a 'cartridge', the higher the ID the more recent the refill.
The second and third Cr-Ap's gather the SUMS of items that have been refilled and items that have been dispensed under the most recent Refill.
If I run the query for Cr-Ap 2 or 3 separately the output would look something like:
ID Amount
1 100
2 1000
3 100
4 0
5 0
etc
Amount would be either the amount of dispensed or refilled items.
Only I don't want to run these queries separately, I want them next to each other.
So what I want is a table that looks like this:
ID Refill Dispense
1 100 1
2 1000 5
3 100 7
4 0 99
5 0 3
etc
My gut tells me to do
INNER JOIN crossaply2 ON crossapply3.ID = crossapply2.ID
But this doesn't work. I'm still new to SQL so I don't exactly know what I can and can't join, what I do know is that you can use crossapply as a join (sorta?). I think that might be what I need to do here, I just don't know how.
But that's not it, there's another complication, there are certain refills where nothing gets dispensed. In these scenarios the crossapply I wrote for dispenses won't return anything for that refillID. With nothing I don't mean NULL, I mean it just skips the refillID. But I'd like to see a 0 in those cases. Because it just skips over those ID's I can't get COALESCE or ISNULL to work, this might also complicate the joining of these two applies. Because an INNER JOIN would skip any line where there is no Dispensed amount, even though there is a Refilled amount Id like to see.
Here is my code:
-- Dispensed SUM and Refilled SUM combined
SELECT [CartridgeRefill].[FK_CartridgeRegistration_Id]
,Refills.Refilled
,Dispenses.Dispensed
FROM [CartridgeRefill]
CROSS APPLY(
SELECT MAX([CartridgeRefill].[Id]) AS RecentRefillID
FROM [CartridgeRefill]
GROUP BY [CartridgeRefill].[FK_CartridgeRegistration_Id]
) AS RecentRefill
CROSS APPLY(
SELECT [CartridgeRefill].[FK_CartridgeRegistration_Id] AS RefilledID
,SUM([CartridgeRefillMedication].[Amount]) AS Refilled
FROM [CartridgeRefillMedication]
INNER JOIN [CartridgeRefill] ON [CartridgeRefillMedication].[FK_CartridgeRefill_Id] = [CartridgeRefill].[Id]
WHERE [CartridgeRefillMedication].[FK_CartridgeRefill_Id] = RecentRefill.RecentRefillID
GROUP BY [CartridgeRefill].[FK_CartridgeRegistration_Id]
) AS Refills
CROSS APPLY(
SELECT [CartridgeRefill].[FK_CartridgeRegistration_Id] AS DispensedID
,SUM([CartridgeDispenseAttempt].[Amount]) AS Dispensed
FROM [CartridgeDispenseAttempt]
INNER JOIN [CartridgeRefill] ON [CartridgeDispenseAttempt].[FK_CartridgeRefill_Id] = [CartridgeRefill].[Id]
WHERE [CartridgeDispenseAttempt].[FK_CartridgeRefill_Id] = RecentRefill.RecentRefillID
GROUP BY [CartridgeRefill].[FK_CartridgeRegistration_Id]
) AS Dispenses
GO
The output of this code is as follows:
1 300 1
1 300 1
1 200 194
1 200 194
1 200 8
1 200 8
1 0 39
1 0 39
1 100 14
1 100 14
1 200 1
1 200 1
1 0 28
1 0 28
1 1000 102
1 1000 102
1 1000 557
1 1000 557
1 2000 92
1 2000 92
1 100 75
1 100 75
1 100 100
1 100 100
1 100 51
1 100 51
1 600 28
1 600 28
1 200 47
1 200 47
1 200 152
1 200 152
1 234 26
1 234 26
1 0 227
1 0 227
1 10 6
1 10 6
1 300 86
1 300 86
1 0 194
1 0 194
1 500 18
1 500 18
1 1000 51
1 1000 51
1 1000 56
1 1000 56
1 500 48
1 500 48
1 0 10
1 0 10
1 1500 111
1 1500 111
1 56 79
1 56 79
1 100 6
1 100 6
1 44 134
1 44 134
1 1000 488
1 1000 488
1 100 32
1 100 32
1 100 178
1 100 178
1 500 672
1 500 672
1 200 26
1 200 26
1 500 373
1 500 373
1 100 10
1 100 10
1 900 28
1 900 28
2 900 28
2 900 28
2 900 28
etc
It is total nonsense that I can't do much with, it goes on for about 20k lines and goes through all the ID's, eventually.
Any help is more than appreciated :)
Looks like overcomplicated a bit.
Try
WITH cr AS (
SELECT [FK_CartridgeRegistration_Id]
,MAX([CartridgeRefill].[Id]) RecentRefillID
FROM [CartridgeRefill]
GROUP BY [FK_CartridgeRegistration_Id]
)
SELECT cr.[FK_CartridgeRegistration_Id], Refills.Refilled, Dispenses.Dispensed
FROM cr
CROSS APPLY(
SELECT SUM(crm.[Amount]) AS Refilled
FROM [CartridgeRefillMedication] crm
WHERE crm.[FK_CartridgeRefill_Id] = cr.RecentRefillID
) AS Refills
CROSS APPLY(
SELECT SUM(cda.[Amount]) AS Dispensed
FROM [CartridgeDispenseAttempt] cda
WHERE cda.[FK_CartridgeRefill_Id] = cr.RecentRefillID
) AS Dispenses;

How to find a specific value in consecutive date

I need your help for a little issue.
I use MS ACCESS to work with a database and I need to resolve a query. My query asks:
Find the CUSTOMER_ID and TRANSC_ID where 2 consecutive value between 200 and 500 WITHIN the same transc_id.
I explain.
I have this table in this format:
CUSTOMER_ID TRANSC_ID VALUE VALUE_DATE
51 10 15 29-12-1999
51 10 20 15-07-2000
51 10 35 18-08-2000
51 10 250 30-08-2000
51 10 13 10-09-2000
51 10 450 15-09-2000
51 11 5 15-09-2000
51 11 23 30-09-2000
51 11 490 10-10-2000
51 11 300 12-10-2000
51 11 85 30-10-2000
51 11 98 01-01-2000
53 10 65 15-10-2000
53 10 14 29-12-2000
And I need just
51 11 490 10-10-2000
51 11 300 12-10-2000
because the two values is consecutive (and both of them is >250 and <500).
How can I make a query in MS ACCESS to obtain this result?
Thank you.
You can get the "next" and "previous" values using correlated subqueries, and then do the comparison:
select t.*
from t
where t.value between 200 and 500 and
( (select top 1 t2.value
from t as t2
where t2.CUSTOMER_ID = t.CUSTOMER_ID and t2.TRANSC_ID = t.TRANSC_ID and
t2.value_date > t.value_date
order by t2.value_date
) between 200 and 500 or
(select top 1 t2.value
from t as t2
where t2.CUSTOMER_ID = t.CUSTOMER_ID and t2.TRANSC_ID = t.TRANSC_ID and
t2.value_date < t.value_date
order by t2.value_date desc
) between 200 and 500
);

SQL oracle - Delete Duplicate Records and update records in other table

Requirement - Delete Duplicate records e.g. from 2 tables and update the records in the other tables.
Input
Table1 Dim_Ctry
PK_Key1 Country
100 Argentina
200 ARGENTINA
300 India
400 INDIA
Table2 Dim_Geo
PK_Key2 Geo
500 Globe
600 GLOBE
700 Market
800 MARKET
900 Unique
Table Fact1
PK_Key15 FK_KEY1 FK_KEY2
1 100 500
2 200 600
3 300 800
4 400 900
Table Fact2
PK_Key16 FK_KEY1 FK_KEY2
5 100 500
6 200 600
7 200 700
8 300 800
output
Table1 Dim_Ctry
PK_Key1 Country
100 Argentina
300 India
Table2 Dim_Geo
PK_Key2 Geo
500 Globe
700 Market
900 Unique
Table Fact1
PK_Key15 FK_KEY1 FK_KEY2
1 100 500
2 100 500
3 300 800
4 300 800
Table Fact2
PK_Key16 FK_KEY1 FK_KEY2 comment
5 100 500
6 100 500
7 100 700
7 300 800
8 1000 2000 no record in dim table just retain
You will need several steps.
Step 1 update related tables
Update all FK_KEY to the min value.
UPDATE Fact1 f1
SET
FK_KEY1 = (SELECT MIN(PK_Key1)
FROM Dim_Ctry dc1
WHERE UPPER(dc1.Country) = (SELECT UPPER(dc2.Country)
FROM Dim_Ctry dc2
WHERE dc2.PK_Key1 = f1.FK_KEY1)
),
FK_KEY2 = (SELECT MIN(PK_Key2)
FROM Dim_Geo dg1
WHERE UPPER(dg1.Geo) = (SELECT UPPER(dg2.Geo)
FROM Dim_Geo dg2
WHERE dg2.PK_Key2 = f1.FK_KEY2)
);
Step 2 delete duplicated
This will delete all duplicated and keep the one with smaller id
DELETE FROM Dim_Ctry dc1
WHERE EXISTS (SELECT PK_Key1
FROM Dim_Ctry dc2
WHERE dc1.PK_Key1 > dc2.PK_Key1
and UPPER(dc1.Country) = UPPER(dc2.Country)
Step 3 update the text
You should update to lower or upper to standard format.
UPDATE Dim_Ctry
SET Country = UPPER(Country)
debug query
SELECT f1.PK_Key15, f1.FK_KEY1, f1.FK_KEY2,
(SELECT UPPER(dc2.Country)
FROM Dim_Ctry dc2
WHERE dc2.PK_Key1 = f1.FK_KEY1
) as CurrentName,
(SELECT MIN(PK_Key1)
FROM Dim_Ctry dc1
WHERE UPPER(dc1.Country) = (SELECT UPPER(dc2.Country) FROM Dim_Ctry dc2 WHERE dc2.PK_Key1 = f1.FK_KEY1) ) as minKey
FROM Fact1 f1

Join Clause With a XOR Statement

I am doing a join and I can't seem to make this XOR to properly work.
SELECT t1.COMPANY, t1.MILES,
CASE WHEN t2.MILES IS NULL THEN t3.MILES
ELSE t2.MILES
END AS MILES2,
CASE WHEN t2.MILES = t1.MILES AND t2.MILES != 9999 THEN t2.FLATRATE
ELSE t3.RATEBASIS
END AS RATE
FROM TABLE1 AS t1
LEFT JOIN TABLE2 AS t2
ON t1.[COMPANY] = t2.[COMPANYCODE] AND (t1.[MILES] = t2.[MILES])
INNER JOIN (
SELECT TOP 1 TRUCKERCODE, MILES, RATEBASIS, FLATRATE FROM TABLE2 WHERE MILES = 9999
) AS t3
ON t1.[COMPANY] = t3.[COMPANYCODE]
I need the ON clause to join if the miles are the same then get the given fields that match otherwise the default data I need to get out of the second table is where the miles is equal to 9999. Right now with that ON clause I get many extra rows where the MILES equals lets say 45, it gets the data from TABLE2 where miles equals 45 and all the data where miles equals 9999. I need it to do one or the other but not both. This is what my tables would look like
TABLE1 TABLE2
ID COMPANY MILES ETC ID COMPANYCODE MILES RATE
1 ILLINI 50 1 ILLINI 50 3.2
2 ILLINI 110 2 ILLINI 110 5.2
3 ILLINI 150 3 ILLINI 150 2.4
4 ILLINI 200 4 ILLINI 200 1.9
5 ILLINI 250 5 ILLINI 9999 1.5
6 ILLINI 300
7 ILLINI 350
8 ILLINI 400
9 ILLINI 450
10 ILLINI 500
Desired Output
COMPANY MILES MILES2 RATE
ILLINI 50 50 3.2
ILLINI 110 110 5.2
ILLINI 150 150 2.4
ILLINI 200 200 1.9
ILLINI 250 9999 1.5
ILLINI 300 9999 1.5
ILLINI 350 9999 1.5
ILLINI 400 9999 1.5
ILLINI 450 9999 1.5
ILLINI 500 9999 1.5
I think this will give you what you want:
SELECT t1.COMPANY, t1.MILES,
CASE WHEN t2.MILES IS NULL THEN t3.MILES
ELSE t2.MILES
END AS MILES2,
CASE WHEN t2.MILES IS NULL THEN t3.RATE
ELSE t2.RATE
END AS RATE
FROM TABLE1 AS t1
LEFT JOIN TABLE2 AS t2
ON t1.[COMPANY] = t2.[COMPANYCODE] AND (t1.[MILES] = t2.[MILES])
INNER JOIN (
SELECT TOP 1 COMPANYCODE, MILES, RATE FROM TABLE2 WHERE MILES = 9999
) AS t3
ON t1.[COMPANY] = t3.[COMPANYCODE]
If there is a match in MILES, then output contains both records from TABLE1, TABLE2. Otherwise, output contains record from left table, i.e. TABLE1 and the specific record from TABLE2 with MILES = 9999.
Please note that TOP 1 is used in the last sub-query just in case more than one records with MILES = 9999 exist in TABLE2.
Output:
COMPANY MILES MILES2 RATE
----------------------------
ILLINI 50 50 3,2
ILLINI 110 110 5,2
ILLINI 150 150 2,4
ILLINI 200 200 1,9
ILLINI 250 9999 1,5
ILLINI 300 9999 1,5
ILLINI 350 9999 1,5
ILLINI 400 9999 1,5
ILLINI 450 9999 1,5
ILLINI 500 9999 1,5
SQL Fiddle Demo here
I would do based on a double left-join.. once for a match, and if not, then default to the 9999 instance
SELECT
T1.*,
coalesce( T2.miles, T3.miles ) as Miles2,
coalesce( T2.rate, T3.rate ) as MileageRate
FROM
TABLE1 T1
LEFT JOIN TABLE2 T2
on T1.Company = T2.CompanyCode
AND T1.Miles = T2.Miles
LEFT JOIN TABLE2 T3
on T1.Company = T3.CompanyCode
AND T3.Miles = 9999
Confirmed working at this SQL Fiddle link