Access Query - Relationship query with 1 to 3 relationship - sql

I have a Microsoft access query I'm trying to set up but have hit a snag. I'd describe this as a select query that has a relationship of 1:3.
I have two tables (A & B), table B has one field (a new part number) that I need to compare against three fields in table A (old/new part numbers). I want to do a select query so I can export as a CSV. In this case I need to match 2ABCD so I get a row with the updated price of $5.
The basic format I'm looking at is this.
From table B, I want to check if UPC4 matches UPC1, UPC2 or UPC3 in table A - matching for the value "2ABCD" in this example. Ideally I want to do this in one query. Thoughts?
TABLE A
UPC1
UPC2
UPC3
Price
ABCD
1ABCD
2ABCD
$4
TABLE B
UPC4
Price
2ABCD
$5
END RESULT SELECT QUERY
UPC1
UPC2
UPC3
UPC4
Price
ABCD
1ABCD
2ABCD
2ABCD
$5

Access supports joins https://learn.microsoft.com/de-de/office/vba/access/concepts/structured-query-language/perform-joins-using-access-sql
So you can combine both tables
SELECT
TableA.UPC1,TableA.UPC2,TableA.UPC3,
TableB.UPC4,TableB.Price
FROM
TableA INNER JOIN TableB ON TableA.UPC1 = TableB.UPC4
OR TableA.UPC2 = TableB.UPC4
OR TableA.UPC3 = TableB.UPC4

Related

Why aren't these two sql statements returning same output?

I'm just getting started with sql and have the objective to transform this:
select X.persnr
from Pruefung X
where X.persnr in (
select Y.persnr
from pruefung Y
where X.matrikelnr <> Y.matrikelnr)
output:
into the same output but using a form of join. I tried it the way below but I can't seem to get "rid" of the cartesian product as far as i can see. Or maybe i misunderstood the above statement what it should actually do. For me the above says "for each unique matrikelnr display all corresponding persnr".
select X.persnr
from Pruefung X
join pruefung y on x.persnr=y.persnr
where x.matrikelnr<>y.matrikelnr
output: A long list (I don't want to fill the entire question with it) - i am guessing the cartesian product from the join
This is the relation I am using.
Edit: Distinct (unless i am using it in the wrong place) won't work because then persnr is only displayed once, thats not the objective though.
Your initial query actually does:
select persnr from Pruefung if the same persnr exists for a a diferent matrikelnr.
"for each unique matrikelnr display all corresponding persnr"
This is achieved using aggregation:
Depending on the DBMS you are using you could use something like (SQL Server uses STRING_AGG, but MySQL uses GROUP_CONCAT)
SELECT matrikelnr,STRING_AGG(matrikelnr,',')
GROUP BY matrikelnr
You cannot easily achieve what you got from a correlated query (your first attempt) by using a join.
Edit:
A join does not result in a "Cartesian product" expect from when there is no join condition (CROSS JOIN).
A join matches two sets based on a join condition. The reason why you get more entries is that the join looks at the join key (PERSNR) and does its matching.
For example for 101 you have 3 entries. That means you will get 3x3 reults.
You then filter out the results for the cases where X.matrikelnr <> Y.matrikelnr If we assume matrikelnr is unique that would mean the row matched with itself. so you will lose 3 results ending up with 3x3 - 3 = 6.
If you want to achieve something in SQL you must first define what you are expecting to use and then use the appropiate tools (in this case correlated queries not joins)
You can write your 1st query with EXISTS instead of IN like:
select X.persnr
from Pruefung X
where exists (
select 1
from pruefung Y
where X.persnr = Y.persnr and X.matrikelnr <> Y.matrikelnr
)
This way it's obvious that this query means:
return all the persnrs of the table for which there exists another
row with the same persnr but different matrikelnr
For your sample data the result is all the persnrs of the table.
Your 2nd query though, does something different.
It links every row of the table with all the rows of the same table with the same persnr but different matrikelnr.
So for every row of the table you will get as many as rows as there are for the same persnrs but different matrikelnrs.
For example for the 1st row with persnr = 101 and matrikelnr = 8532478 you will get 2 rows because there are 2 rows in the table with persnr = 101 and matrikelnr <> 8532478.
You are right. It's the cartesian product's fault. Suppose you have persnr 1,1,1,2,2,2 in the first table and persnr 1,1,1,2,2 in the second. How many lines are you expecting to be returned?
In pdeuso-code it would go like this
Select
...
WHERE persnr in (second table)
-- 6 lines
Select persnr
FROM ...
JOIN ... ON a.persnr = b.persnr
-- 3X3 + 3X2 = 15 lines.
SELECT DISTINCT persnr
FROM ...
JOIN ... ON a.persnr = b.persnr
-- 2 lines (1 and 2)
Take your pick

Concatenating codes to obtain sum

I've been for tha past 2 days trying to solve this problem but can't even seem to find the right terms to google it.
I have 3 tables.
This one, with client codes that changed:
ActualCode=111111111 PreviousCode=44444444
And these two tables with value 1 and value 2:
PreviousCode=11111111, Value1= 50,00, Value2= 0,00
ActualCode=44444444 , Value1= 0,00, Value2 = 50,00
I need to sum the values for each relation of Previous and Actual codes from the first table.
I.E.
For
ActualCode=11111111, PreviousCode=44444444
I need to be able to get:
Code=11111111 Value1=50,00 Value2=50,00
Looking forward for your answer :D
Thanks,
P
You can join the tables and sum the values:
select c.actualcode,
sum(ac.value1) + sum(pc.value1) as value1,
sum(ac.value2) + sum(pc.value2) as value2
from codes c
join actualcodes ac on c.actualcode = ac.actualcode
join previouscodes pc on c.previouscode = pc.previouscode
group by c.actualcode;
Rextester Demo
If you could have values in the main table that don't have corresponding rows in the values tables, then you should use outer joins instead.

SQL Case with calculation on 2 columns

I have a value table and I need to write a case statement that touches 2 columns: Below is the example
Type State Min Max Value
A TX 2 15 100
A TX 16 30 200
A TX 31+ 500
Let say I have another table that has the following
Type State Weight Value
A TX 14 ?
So when I join the table , I need a case statement that looks at weight from table 2 , type and state - compare it to the table 1 , know that the weight falls between 2 and 15 from row 1 and update Value in table 2 with 100
Is this doable ?
Thanks
It returns 0 if there aren't rows in this range of values.
select Type, State, Weight,
(select coalesce(Value, 0)
from table_b
where table_b.Type = table_a.Type
and table_b.State = table_a.State
and table_a.Value between table_b.Min and table_b.Max) as Value
from table_a
For an Alteryx solution: (1) run both tables into a Join tool, joining on Type and State; (2) Send the output to a Filter tool where you force Weight to be between Min and Max; (3) Send that output to a Select tool, where you grab only the specific columns you want; (since the Join will give you all columns from all tables). Done.
Caveats: the data running from Join to Filter could be large, since you are joining every Type/State combination in the Lookup table to the other table. Depending on the size of your datasets, that might be cumbersome. Alteryx is very fast though, and at least we're limiting on State and Type, so if your datasets aren't too large, this simple solution will work fine.
With larger data, try to do it as part of your original select, utilizing one of the other solutions given here for your SQL query.
Considering that Min and Max columns in first table are of Integer type
You need to use INNER JOIN on ranges
SELECT *
FROM another_table a
JOIN first_table b
ON a.type = b.type
AND a.State = b.State
AND a.Weight BETWEEN b.min AND b.max

Select the FIRST record with a value greater than XXX

OK, another newbie SQL question which i'm sure has a simple solution and i'll kick myself when someone posts the answer!
I have two tables as follows
PRICE_DTA
PRC_DATE PRC_TIME PRC_PRICE PRC_ITEM
2008-01-01 06.00.00 1.05 JUMPER
2008-01-01 09.00.00 1.20 JUMPER
2008-01-25 17.00.00 1.75 JUMPER
2008-01-02 09.00.00 2.25 TROUSERS
2008-10-25 12.00.00 2.95 TROUSERS
SALE_DTA
TRN_DATE TRN_TIME TRN_PRICE_PAID TRN_ITEM
2008-01-01 08.30.00 JUMPER
2008-01-03 10.00.00 JUMPER
2008-01-03 17.00.00 JUMPER
2008-01-01 13.00.00 TROUSERS
2008-01-02 09.00.00 TROUSERS
The way the prices work is that you get the NEXT available price(prices aren't set until after the purchase because we bulk all the orders up and get a cheaper price the more we buy in one go). So, in the example the 08.30.00 purchase on 2008-01-01 will have been for 1.20 because that is the first available price after the purchase date/time
So, I need to populate the prices in the SALE_DTA table using the TRN_DATE/TRN_TIME fields to go an get the next available price off the PRICE_DTA tables. NOTE: The DATE and TIME fields on both tables are CHAR fields not date/timestamp fields
I can concatenate the date and time easily enough but i'm not sure how to find the FIRST record on PRICE_DTA with a date/time stamp greater than that. I know on UNISYS DMS II I can use a 'FIND NEXT GREATER THAN' but can't find a similar command in SQL?
I'm happy to create a temporary table as part of the solution if that makes it simpler.
The generic SQL solution for this can be done with a couple of joins:
SELECT
* --TODO - Pick appropriate columns
FROM
SALE_DTA s
INNER JOIN
PRICE_DTA p
ON
p.PRC_ITEM = s.TRN_ITEM and
(p.PRC_DATE > s.TRN_DATE or
(p.PRC_DATE = s.TRN_DATE and
p.PRC_TIME > s.TRN_TIME
))
LEFT JOIN
PRICE_DTA p2
ON
p2.PRC_ITEM = s.TRN_ITEM and
(p2.PRC_DATE > s.TRN_DATE or
(p2.PRC_DATE = s.TRN_DATE and
p2.PRC_TIME > s.TRN_TIME
)) and
(p2.PRC_DATE < p.PRC_DATE or
(p2.PRC_DATE = p.PRC_DATE and
p2.PRC_TIME < p.PRC_TIME
))
WHERE
p2.PRC_ITEM IS NULL
Hopefully, you can see the logic here. The INNER JOIN is used to match rows in SALE_DTA with all rows in PRICE_DTA that occur afterwards. We then do a second join (the LEFT JOIN) to this PRICE_DTA again, this time trying to locate a row with this join (p2) such that it still occurs after the s date/time, but occurs before the p date/time.
Finally, in the WHERE clause, we eliminate any rows where this LEFT JOIN actually succeeded. Therefore, by deduction, we know that the row that we matched in p is the earliest row from PRICE_DTA which occurs after the SALE_DTA date/time.
You can certainly get the data required but DB2 don't support JOIN with UPDATE statement. So you can take a different route like
Create a auxiliary table
create table SALE_DTA_temp(TRN_DATE,TRN_TIME,TRN_PRICE_PAID,TRN_ITEM)
Do a insert into temp table from the query
insert into SALE_DTA_temp
select sd.TRN_DATE,
sd.TRN_TIME,
tab.max_PRC_PRICE as TRN_PRICE_PAID,
sd.TRN_ITEM
from SALE_DTA sd
join
(
select PRC_DATE, max(PRC_PRICE) as max_PRC_PRICE
from PRICE_DTA
group by PRC_DATE
) tab on sd.TRN_DATE = tab.PRC_DATE
Drop the old table
drop table SALE_DTA
Rename the table
RENAME TABLE SALE_DTA_temp TO SALE_DTA

SQL Server: Subtraction between 2 queries

i have 2 queries, where in one table the amount is shown for cars such as
Amount_Table Cars
800 Car A
900 Car B
2100 Car C
Second Table shows discount respectively for Car A, B & C.
Discount_table
40
10
80
I wish to have a final query where in the Amount-Discount values are displayed
The amount table has one query made and discount table has another query. hence i wish to do
(amount-query) - (discount query)
I did
Select ( (amount-query) - (discount-query))
but that threw error of
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
Please help!
try something like this:
Select AmountTable.Amount-isnull(DiscountTable.Discount, 0)
from AmountTable left join
on AmountTable.Car = DiscountTable.Car
You cannot "subtract" queries. You have to do a join between tables (or subqueries), and make expressions using columns' names.
You need to join:
SELECT *
,cars_table.amount - discounts_table.discount
FROM cars_table
INNER JOIN discounts_table
ON cars.some_key = discounts_table.some_key