How to write sql for this? - sql

I've tables like this
Customer
-ID
-name
-address
Business
-ID
-name
-type
Discount
-ID
-amount
-BusinessID
-position
UsedDiscounts
-CustomerID
-DiscountID
Business has many discounts.
User has used many discounts, record of which is in UsedDiscounts.
User can only use discount in order, defined by position. Discount 1, then Discount 2. So even if business has 10 discounts out, the one Customer qualifies for is Position of used discount by that business + 1.
Goal: get all discounts user qualify for.
My approach was to do left exclusion join on Discounts and Used Discounts.
So get all discounts minus used ones, then somehow do min on position and get all "qualified" ones. However, I might be able to pull this off in SQL I have no idea how..
Sample Incomplete SQL looks like this
SELECT *, min(gd.position) FROM
(SELECT * FROM "Deals" as d WHERE (d.active = true) AND (d.latitude BETWEEN 40 AND 41) AND (d.longitude BETWEEN -75 AND -70)) AS gd
LEFT JOIN
(SELECT du."DealId" FROM "DealsUsed" AS du WHERE du."CustomerId" = 1) AS bd
ON gd.id = bd."DealId"
WHERE bd."DealId" IS NULL
GROUP BY gd."UserId";
gives wrong output
Sample data:
Customer
--------
id name address
0 Tobby 93903903
1 Emi 3839039
2 Loop 393030
Business
--------
id name type
0 Cool flower
1 Corner car
2 New deli
3 Side printing
4 Big car
Discount
--------
id amount businessId position
0 10 0 0
1 22 3 1
2 10 3 2
3 43 2 0
4 23 5 0
5 10 5 1
Used Discount
----------
customerId discountId
1 2
outcome for customer 1 , emi, shouuld be
Discounts
--------
id amount businessId position
0 10 0 0
4 23 5 0
3 43 2 0
5 10 5 1

Your left join does not take affect because you use where clause in the second query:
Remove where clause:
SELECT *, min(gd.position) FROM
(SELECT * FROM "Deals" as d WHERE (d.active = true) AND (d.latitude BETWEEN 40 AND 41) AND (d.longitude BETWEEN -75 AND -70)) AS gd
LEFT JOIN
(SELECT du."DealId" FROM "DealsUsed" AS du) AS bd
ON gd.id = bd."DealId"
WHERE bd."DealId" IS NULL
GROUP BY gd."UserId";
using Group by with Select * is not advisable. Just select the fields you need. Something like:
SELECT gd."UserId", min(gd.position) FROM
(SELECT * FROM "Deals" as d WHERE (d.active = true) AND (d.latitude BETWEEN 40 AND 41) AND (d.longitude BETWEEN -75 AND -70)) AS gd
LEFT JOIN
(SELECT du."DealId" FROM "DealsUsed" AS du) AS bd
ON gd.id = bd."DealId"
WHERE bd."DealId" IS NULL
GROUP BY gd."UserId";
You can use where clause after on like:
SELECT gd."UserId",bd."CustomerId", min(gd.position) FROM
(SELECT * FROM "Deals" as d WHERE (d.active = true) AND (d.latitude BETWEEN 40 AND 41) AND (d.longitude BETWEEN -75 AND -70)) AS gd
LEFT JOIN
(SELECT du."DealId" FROM "DealsUsed" AS du) AS bd
ON gd.id = bd."DealId"
WHERE bd."DealId" IS NULL and bd."CustomerId" = 1
GROUP BY gd."UserId";

Related

How to multiply data to get value | Oracle |

I have 2 table Table 1 : Employee and Table 2 : Department
Below is the join condition i have used
select e.empsal,d.rate from employee e left join on department d where e.empid = d.depid
Based on d.rate the value of e.empsal should get multiple , upto join condition i was able to do after that where to add case is not getting , if you see drate value will be anything
if their are 25 drate values like : 1 2 4 6 7 9 14 36 67 8 2 4 19 11 8
It is not feasible to write that many times if conditions
case drate
if 0 then e.empsal*1
if 1 then e.empsal*10
if 2 then e.empsal*100
if 4 then e.empsal*1000
if 6 then e.empsal*1000000
...
Example : Below is the logic and its Output expected:
if d.rate value is 0 then new_empsal=e.empsal * 1
if d.rate value is 1 then new_empsal=e.empsal * 10
if d.rate value is 2 then new_empsal=e.empsal * 100
if d.rate value is 3 then new_empsal=e.empsal * 1000
will go on ...
So the new values of e.empsal should be below
EMPSAL,DRATE,NEW_EMPSAL
1200,0,1200
2345,1,23450
1678,3,1678000
2219,6,2219000000
2458,1,24580
2697,2,269700
2936,1,29360
3175,4,31750000
3414,7,34140000000
3653,2,365300
3892,1,38920
4131,1,41310
4566,0,4566
4131,4,41310000
1000,6,1000000000
345,1,3450
2345,5,234500000
1123,0,1123
990,3,990000
1100,3,1100000
345,7,3450000000
2345,2,234500
How to achieve this below output using above condition ?
Are you looking for a case expression?
select e.*,
(case d.rate
when 0 then 1 when 1 then 10 when 2 then 100 when 3 then 1000 when 4 then 10000 when 6 then 1000000
end) * e.empsal
from employee e left join
department d
on e.depid = d.depid;
Note that this fixes your syntax using where instead of on. I also changed the join condition, because matching empid to depid doesn't make much sense to me.
Or more concisely using power():
select e.*, power(10, d.rate) * e.empsal
from employee e left join
department d
on e.dep = d.depid;

how can i get an accurate count based on max date when joining 3 tables when one of the join fields is many to 1 in oracle?

So, I have 3 tables that I am attempting to get counts for based on a groupid, and a task code. There are a few issues I am having as some of the relationships are many to one, which I think is somehow inflating my counts. I will list my 3 tables with the pertinent attributes.
task_table contains:
task_code - would like to get the counts of each one in a group id, would like to use the latest instance basedon event date.
sol_id -used to join to worktable; many sol_id to one m_id is possible
edate -need to use to get one record
cur_id - where cur_id = 1 in the where clause
worktable contains:
sol_id - used to join to task_table
m_id - used to join to grouptable
grouptable contains:
m_id
groupid- used to group the task_code to get count
I'd like the end result to look like:
group_id task_count task
5555 45 A
5555 4 N
5624 67 A
5624 23 O
5624 42 X
I have been attempting to run a number of queries, but the counts I am getting back do not look correct. I am concerned that it is somehow returning more than one instance of the m_id somehow? Here is the query in question:
select c.groupid, count(c.groupid) group_count, a.task_code from task_table a
join worktable b
on a.sol_id = b.sol_id
join grouptable c
on b.m_id= c.m_id
where a.cur_id = 1 and a.task_code is not null
group by c.groupid, a.task_code;
If I add 'edate = (select max(edate) from task_table)' in the where clause, it returns an empty table.
I am unsure how to incorporate edate to get only the newest record that fits the criteria in the where clause. The reason I think I want to use this is because there could be more than one sol_id that is associated with a m_id, so i'd just like to include only the newest record with a cur_id in the count. Thank you for your time.
sample data
task_table
task_code sol_id edate cur_id
A 23 6/7/09 1
A 24 6/4/09 1
A 23 6/10/09 0
B 45 6/2/09 1
B 42 6/3/09 1
C 34 10/8/10 0
C 83 9/10/09 1
work table
sol_id m_id
23 1234
24 1234
45 1832
42 1343
83 7623
group table
m_id group_id
1234 A76
1832 Y23
1343 A76
7623 Y23
looking at these tables, the result should look like the following
group_id task_count task
A76 2 A
Y23 1 C
( A76 should only count sol_id 23 and 42)
( Y23 should only count sol_id 83)
So, there's a conflict in your requested data result. According to your own sample, A76 should have a task_count of 2: sol_id 23, which has Task A, and sol_id 42, which has Task B. It's not possible to have it return a row like you have at your example result table because it would need to group by TASK_CODE, which means losing the COUNT(task_code). Can't have it both ways.
In order to obtain only the most recent edate, I did a separate calculation to location that max(edate) by task_code, then joined it back to obtain the sol_id. If this isn't accurate for your data set, you'll need to determine another way of obtaining max(edate). This works for your sample set.
with recentTasks as (
select task_code, max(edate) as recentDate
from task_table m
where cur_id = 1
and task_code is not null
group by task_code
), recentTaskWithSols as (
select m.task_code, m.recentDate as edate, t.sol_id
from recentTasks m
join task_table t on m.task_code = t.task_code AND m.recentDate = t.edate
where t.cur_id = 1
)
select c.group_id,
count(a.sol_id) task_count
from group_table c
join work_table b on c.m_id = b.m_id
join recentTaskWithSols a on b.sol_id = a.sol_id
group by c.group_id;
gives the result:
+------------------------+
| GROUP_ID | TASK_COUNT |
+------------------------+
| A76 | 2 |
| Y23 | 1 |
+-----------+------------+
Demo here.

Left Join Display All Data From Table1 and Table2

I am trying to do a left join so that I get all of my rows from Table 1 even if there is no value corresponding to it in the second table.
My structures are:
Location Table:
ID LocName
1 Trk1
2 Trk2
3 Trk3
4 Unk
Quantity Table:
ID PartID Quantity LocationID
1 1 2 1
2 3 12 2
3 2 6 1
4 6 8 3
5 6 5 1
I am trying to join but also make a query on a specific PartID. My query is:
SELECT
INV_LOCATIONS.ID AS LocationID,
INV_LOCATIONS.NAME AS LocationName,
INV_QUANTITY.QUANTITY AS Quantity
FROM INV_LOCATIONS
LEFT JOIN INV_QUANTITY ON INV_LOCATIONS.ID = INV_QUANTITY.LOCATION_ID
WHERE INV_QUANTITY.PART_ID = 1;
My output right now would be:
ID LocName Quantity
1 Trk1 5
3 Trk3 8
The Desired output is:
ID LocName Quantity
1 Trk1 5
2 Trk2 NULL/0
3 Trk3 8
4 Unk NULL/0
I assume it is because I have the WHERE INV_QUANTITY.PART_ID = 1 and that is forcing it to be in the quantity table. I need to be able to verify it is on the right part but how do I also include it if it doesn't exist. I know I have done something very similar before but I cannot remember which project and so I cannot find the code anywhere.
You need to move the filtering logic to the ON clause:
SELECT il.ID AS LocationID, il.NAME AS LocationName,
iq.QUANTITY AS Quantity
FROM INV_LOCATIONS il LEFT JOIN
INV_QUANTITY iq
ON il.ID = iq.LOCATION_ID AND iq.PART_ID = 1;

SQL join query for a view with sum of columns across 3 tables

I have 3 tables as below
Table - travel_requests
id industry_id travel_cost stay_cost other_cost
1 2 1000 500 200
2 4 4000 100 200
3 5 3000 0 400
4 1 3000 250 100
5 1 200 100 75
Table - industry_tech_region
id industry_name
1 Auto
2 Aero
3 Machinery
4 Education
5 MTV
Table - industry_allocation
id industry_id allocation
1 1 500000
2 2 300000
3 3 500000
4 4 300000
5 5 500000
6 1 200000
I want to create a view which has 3 columns
industry_name, total_costs, total_allocation
I created a view as below
SELECT industry_tech_region.industry_name,
SUM(travel_requests.travel_cost + travel_requests.stay_cost + travel_requests.other_cost) AS total_cost,
SUM(industry_allocation.allocation) AS total_allocation
FROM industry_tech_region
INNER JOIN industry_allocation
ON industry_tech_region.id = industry_allocation.industry_id
INNER JOIN travel_requests
ON industry_tech_region.id = travel_requests.industry_id
GROUP BY industry_tech_region.industry_name
But the result I get is as below which is incorrect
industry_name total_cost total_allocation
Aero 1700 300000
Auto 7450 1400000 (wrong should be 3725 and 700000)
Education 4300 300000
MTV 3400 500000
This is probably happening because there are 2 entries for industry_id 1 in the travel_requests table. But they should be counted only once.
Please let me know how do we correct the view statement.
Also I want to add another column in view which is remaining_allocation which is difference of total_allocation and total_cost for each industry.
you shoud join the sum (and not sum the join)
select
a.industry_name
, t1.total_cost
, t2.total_allocation
from dbo.industry_tech_region a
left join (
select dbo.travel_requests.industry_id
, SUM(dbo.travel_requests.travel_cost + dbo.travel_requests.stay_cost + dbo.travel_requests.other_cost) AS total_cost
FROM bo.travel_requests
group by dbo.travel_requests.industry_id
) t1 on a.id = t1.industry_id
left join (
select dbo.industry_allocation.industry_id
, SUM(dbo.industry_allocation.allocation) AS total_allocation
from dbo.industry_allocation
group by dbo.industry_allocation.industry_id
) t2 on a.id = t2.industry_id
this happen because you have two entry for the industry_id 1 and then the row are joined two time if you use the subquery for aggreated the row this can't happen ...
I have used left join because seems that not all the industry_id match for the 3 tables ..
You can use this approach too (without the ORDER BY because views do not allow it).
;WITH q AS (
SELECT industry_id
, sum(allocation) AS total_allocation
FROM #industry_allocation
GROUP BY industry_id
)
SELECT #industry_tech_region.industry_name
, isnull(SUM(#travel_request.travel_cost
+ #travel_request.stay_cost
+ #travel_request.other_cost),0.0) AS total_cost
,q.total_allocation AS total_allocation
FROM #industry_tech_region
LEFT JOIN q ON #industry_tech_region.id = q.industry_id
LEFT JOIN #travel_request ON #industry_tech_region.id = #travel_request.industry_id
GROUP BY #industry_tech_region.industry_name,q.total_allocation
ORDER BY industry_name

Identify same amounts over different users

Consider the following table Orders:
OrderID Name Amount
-----------------------
1 A 100
2 A 5
3 B 32
4 C 4000
5 D 701
6 E 32
7 F 200
8 G 100
9 H 12
10 I 17
11 J 100
12 J 100
13 J 11
14 A 5
I need to identify, for each unique 'Amount', if there are 2 or more users that have ordered that exact amount, and then list the details of those orders. So the desired output would be:
OrderID Name Amount
---------------------
1 A 100
8 G 100
11 J 100
12 J 100
3 B 32
6 E 32
please note that user A has ordered 2 x an order of 5 (order 2 and 14) but this shouldn't be in the output as it is within the same user. Only if another user would have made a order of 5, it should be in the output.
Can anyone help me out?
I would just use exists:
select o.*
from orders o
where exists (select 1
from orders o2
where o2.amount = o.amount and o2.name <> o.name
);
You can do :
select t.*
from table t
where exists (select 1 from table t1 where t1.amount = t.amount and t1.name <> t.name);
If you want only selected field then
SELECT Amount,name,
count(*) AS c
FROM TABLE
GROUP BY Amount, name
HAVING c > 1
ORDER BY c DESC
if you want full row
select * from table where Amount in (
select Amount, name from table
group by Amount, name having count(*) > 1)