select with multiple conditiions in where in - sql

With tables table1 and table2need select the rows of table1 for those locationIds and hours where tier in table2 is high
table1
+------------+------+---------+----------+
| locationId | hour | metric1 | metric2 |
+------------+------+---------+----------+
| 1111 | 10 | 200 | 40 |
| 1111 | 9 | 300 | -20 |
| 1111 | 11 | 1800 | 300 |
| 1122 | 9 | 600 | 800 |
| 1122 | 11 | 2300 | -10 |
| 1133 | 8 | 10000 | 30 |
+------------+------+---------+----------+
table2
+------------+------+-------+---------+
| locationId | hour | value | tier |
+------------+------+-------+---------+
| 1111 | 10 | 1300 | high |
| 1111 | 9 | 900 | medium |
| 1111 | 11 | 200 | low |
| 1122 | 9 | 100 | low |
| 1122 | 11 | 2300 | high |
| 1133 | 8 | 1400 | high |
+------------+------+-------+---------+
If it was one column - say locationId I could have done something like
select * from table1
where locationId in (select locationId from table2 where tier='high');
How do I do this when the locationId, hour pair need to be compared?
output
+------------+------+---------+----------+
| locationId | hour | metric1 | metric2 |
+------------+------+---------+----------+
| 1111 | 10 | 200 | 40 |
| 1122 | 11 | 2300 | -10 |
| 1133 | 8 | 10000 | 30 |
+------------+------+---------+----------+

I think a simple join should handle this:
SELECT t1.*
FROM table1 t1
INNER JOIN table2 t2
ON t1.locationId = t2.locationId AND
t1.hour = t2.hour
WHERE t2.tier = 'high'
Output:
Demo here:
Rextester

The INNER JOIN keyword selects all rows from both tables as long as there is a match between the columns in both tables.
select * from table1 t1
inner join table2 t2 on t1.locationId =t2.locationId
where t2.tier='high'

Use EXISTS method to get your result :
SELECT *
FROM table1 TBL1 WHERE EXISTS
(
SELECT 1 FROM table2 TBL2 WHERE TBL1.locationId = TBL2.locationId AND
TBL1.hour = TBL2.hour AND TBL2.tier = 'high'
)

Related

SQL query for the store with the minimum price of an item in the city

I have this table
(city,storeID,itemID,price)
I need to return for each city,itemID the storeID with the minimum price for the item and the minimum price itself ( city,itemID,storeIDmin,minprice).
Can someone help me with this query ?
Thanks!
I solved this with Join and Subquery (Also possible to use "WITH AS" Clause if you work on oracle DB):
SELECT table1.city, table1.itemID, table1.storeID as storeIDmin, subquery.min_price
FROM table1
JOIN (select city, itemID, min(price) as min_price from table1
group by city,itemID) AS subquery
ON table1.city = subquery.city
AND table1.itemID = subqueryitemID
AND table1.price =
subquery.min_price
the result for example:
+------+---------+--------+-------+
| city | storeID | itemID | price |
+------+---------+--------+-------+
| 1 | 1 | 1 | 70 |
| 1 | 2 | 1 | 60 |
| 2 | 1 | 1 | 100 |
| 2 | 1 | 2 | 90 |
| 2 | 2 | 1 | 88 |
| 3 | 1 | 1 | 70 |
+------+---------+--------+-------+
will result:
+------+--------+----------+-------+
| city | itemID | storeMin | price |
+------+--------+----------+-------+
| 2 | 1 | 1 | 88 |
| 3 | 1 | 1 | 70 |
| 2 | 2 | 1 | 90 |
| 1 | 1 | 2 | 60 |
+------+--------+----------+-------+
You can approach this with a correlated subquery:
select t.*
from t
where t.price = (select min(t2.price) from t t2 where t2.itemId = t.itemId);

Need query for JOIN four tables with some conditions?

I have the following four tables:
1) mls_user
2) mls_category
3) bonus_point
4) mls_entry
In mls_user table values are like below:
*-------------------------*
| id | store_id | name |
*-------------------------*
| 1 | 101 | sandeep |
| 2 | 101 | gagan |
| 3 | 102 | santosh |
| 4 | 103 | manu |
| 5 | 101 | jagveer |
*-------------------------*
In mls_category table values are like below:
*---------------------------------*
| cat_no | store_id | cat_value |
*---------------------------------*
| 20 | 101 | 1 |
| 21 | 101 | 4 |
| 30 | 102 | 1 |
| 31 | 102 | 2 |
| 40 | 103 | 1 |
| 41 | 103 | 1 |
*---------------------------------*
In bonus_point table values are like below:
*-----------------------------------*
| user_id | store_id | bonus_point |
| 1 | 101 | 10 |
| 4 | 101 | 5 |
*-----------------------------------*
In mls_entry table values are like below:
*-------------------------------------------------------*
| user_id | store_id | category | distance | status |
*-------------------------------------------------------*
| 1 | 101 | 20 | 10 | Approved |
| 1 | 101 | 21 | 40 | Approved |
| 1 | 101 | 20 | 10 | Approved |
| 2 | 101 | 20 | 5 | Approved |
| 3 | 102 | 30 | 10 | Approved |
| 3 | 102 | 31 | 80 | Approved |
| 4 | 101 | 20 | 15 | Approved |
*-------------------------------------------------------*
And I want below output:
*--------------------------------------------------*
| user name | Points | bonus Point | Total Point |
*--------------------------------------------------*
| Sandeep | 30 | 10 | 40 |
| Santosh | 30 | 0 | 30 |
| Manu | 15 | 5 | 20 |
| Gagan | 5 | 0 | 5 |
| Jagveer | 0 | 0 | 0 |
*--------------------------------------------------*
I tell the calculation of how the points will come for user Sandeep.
Points = ((10+10)/1 + 40/4)=30
Here 1 and 4 is cat value which comes from mls_category.
I am using below code for a particular user but when i
SELECT sum(t1.totald/c.cat_value) as total_distance
FROM mls_category c
join (
select sum(distance) totald, user_id, category
FROM mls_entry
WHERE user_id=1 AND store_id='101' AND status='approved'
group by user_id, category) t1 on c.cat_no = t1.category
I have created tables in online for checking
DEMO
Computing the points (other than the bonus points) requires a separate join between the mls_entry and mls_category tables. I would do this in a separate subquery, and then join this to the larger query.
Here is one approach:
SELECT
u.name,
COALESCE(t1.points, 0) AS points,
COALESCE(b.bonus_point, 0) AS bonus_points,
COALESCE(t1.points, 0) + COALESCE(b.bonus_point, 0) AS total_points
FROM mls_user u
LEFT JOIN
(
SELECT e.user_id, SUM(e.distance / c.cat_value) AS points
FROM mls_entry e
INNER JOIN mls_category c
ON e.store_id = c.store_id AND e.category = c.cat_no
GROUP BY e.user_id
) t1
ON u.id = t1.user_id
LEFT JOIN bonus_point b
ON u.id = b.user_id
ORDER BY
total_points DESC;
This is the output I am getting from the above query in the demo you setup:
The output does not match exactly, because you have (perhaps) a typo in Santosh's data in your question, or otherwise the expected output in your question has a typo.

Two tables join by number interval

Hello I have two SQL tables, first look like this:
| id | position | name |
|----|----------|-------|
| 1 | 553 | John |
| 2 | 876 | James |
| 3 | 999 | Jack |
And second like this:
| id | class | interval_start | interval_end |
|----|--------|----------------|--------------|
| 1 | class1 | 500 | 580 |
| 2 | class2 | 600 | 700 |
| 3 | class3 | 900 | 1200 |
And I would like to add class from second table to first table based on interval (if t1.position is bigger than start and smaller than end - add another column with class)
So result should be:
| id | position | name | class |
|----|----------|-------|--------|
| 1 | 553 | John | class1 |
| 2 | 876 | James | |
| 3 | 999 | Jack | class3 |
I am not sure how to do that, because I have very large tables (millions of rows). I can also download the data and process it manually in Python.
Which way do you think will be optimal for this task?
You can use left join:
select t1.*, t2.class
from table1 t1 left join
table2 t2
on t1.position between t2.interval_start and t2.interval_end;
You can try below SQL, it will fulfill your requirement.
select t1.id,
t1.position,
t1.name,
t2.class
from table1 t1
left join table2 t2
on (t1.id = t2.id
and t1.position between t2.interval_start and t2.interval_end
);
Output:
+------+----------+-------+--------+
| id | position | name | class |
+------+----------+-------+--------+
| 1 | 553 | John | class1 |
| 3 | 999 | Jack | class3 |
| 2 | 876 | James | NULL |
+------+----------+-------+--------+

Transposing Data SQL

The data looks similar to this:
+----+------+-----------+-------+---------+---------+--------+
| ID | Unit | Floorplan | Sq Ft | Name | Amenity | Charge |
+----+------+-----------+-------+---------+---------+--------+
| 1 | 110 | A1 | 750 | Alan | GARAGE | 50 |
| 2 | | | | | RENT | 850 |
| 3 | | | | | PEST | 2 |
| 4 | | | | | TRASH | 15 |
| 5 | | | | | TOTAL | 20 |
| 6 | 111 | A2 | 760 | Bill | STORAGE | 35 |
| 7 | | | | | GARAGE | 50 |
| 8 | | | | | RENT | 850 |
| 9 | | | | | PEST | 2 |
| 10 | | | | | TOTAL | 15 |
| 11 | 112 | A3 | 770 | Charlie | PETRENT | 20 |
| 12 | | | | | STORAGE | 35 |
| 13 | | | | | GARAGE | 50 |
| 14 | | | | | RENT | 850 |
| 15 | | | | | TOTAL | 2 |
+----+------+-----------+-------+---------+---------+--------+
I am new to SQL and trying my best using Microsoft Access, but I need help.
The data needs to look like this:
My first step is to separate the units from the rest with
SELECT * FROM table WHERE Unit <> NULL;
and after that I've usually just hard-input the rest.
My idea was as follows:
INSERT INTO table
VALUES (NULL,NULL,...,'Pest',$2)
FROM table
WHERE NOT EXIST 'Pest' BETWEEN x AND y
/* where x = Total 1 and y = Total 2*/
Am I on the right track? I probably need a loop or a join, but I'm not at that level yet.
You can use a crosstab query, though a bit convoluted it is:
TRANSFORM
Sum(TableUnit.Charge) AS SumOfCharge
SELECT
S.Unit,
S.Floorplan,
S.SqFt,
S.Name,
S.Amenity
FROM
TableUnit,
(SELECT
Q.Id,
Val(DMax("Id","TableUnit","Id<=" & Q.[Id] & " And Unit Is Not Null")) AS ParentId
FROM TableUnit As Q) AS T,
(SELECT
TableUnit.Id,
TableUnit.Unit,
TableUnit.Floorplan,
TableUnit.SqFt,
TableUnit.Name,
TableUnit.Amenity
FROM
TableUnit
WHERE
TableUnit.Unit Is Not Null) AS S
WHERE
TableUnit.Id=[T].[Id]
AND
T.ParentId)=[S].[Id]
GROUP BY
T.ParentId,
S.Unit,
S.Floorplan,
S.SqFt,
S.Name,
S.Amenity
PIVOT
TableUnit.Amenity In
("Garage","Pest","Trash","PetRent","Storage","Rent");
Your test data differs a little from your expected output, so:
My MSAccess is rather rusty, but something like this should work:
SELECT t0.Unit, t0.Floorplan, t0.[Sq Ft], t0.Name, t0.Amenity
, SUM(IIF(tM.Amenity = 'GARAGE', Charge, 0)) AS [Garage]
, SUM(IIF(tM.Amenity = 'PEST', Charge, 0)) AS [Pest]
FROM (
SELECT t1.id AS id0, MIN(t2.id) AS idN
FROM t AS t1
INNER JOIN t AS t2 ON t1.id < t2.id
WHERE t1.Unit <> '' AND t2.Unit <> ''
) AS groups
INNER JOIN t AS t0 ON t0.id = groups.id0
LEFT JOIN t AS tM ON tM.id > groups.id0 AND tm.id < groups.idN
GROUP BY t0.Unit, t0.Floorplan, t0.[Sq Ft], t0.Name, t0.Amenity
;
Though, if I remember correctly, and it hasn't changed in newer versions; you can't have true subqueries and will need to make groups a separate query you can join to as if it were a table/view.

SQL JOIN value less than or equal to number

I am trying to join two tables based on logic where each record from the first table would pull a value <= to a number in the second table using a join. I am curious if this can be achieved in an efficient manner in SQL. I found some questions focused on dates, but nothing quite similar.
I have two tables. 'Table1' is the primary table
**Table1**
+---------+---------+--------+
| Product | Carrier | Weight |
+---------+---------+--------+
| Z | B | 600 |
+---------+---------+--------+
| Z | B | 350 |
+---------+---------+--------+
| Y | A | 150 |
+---------+---------+--------+
| X | A | 75 |
+---------+---------+--------+
| Y | B | 10 |
+---------+---------+--------+
| X | A | 40 |
+---------+---------+--------+
'Table2' is the lookup table
**Table2**
+---------+--------+------+
| Carrier | Weight | Cost |
+---------+--------+------+
| A | 50 | 2.50 |
+---------+--------+------+
| A | 100 | 2.00 |
+---------+--------+------+
| A | 200 | 1.75 |
+---------+--------+------+
| B | 200 | 1.85 |
+---------+--------+------+
| B | 400 | 1.50 |
+---------+--------+------+
| B | 600 | 1.35 |
+---------+--------+------+
The result would apply the closest cost from table2 for a weight <= Table1
**Result**
+---------+---------+--------+------+
| Product | Carrier | Weight | Cost |
+---------+---------+--------+------+
| Z | B | 600 | 1.35 |
+---------+---------+--------+------+
| Z | B | 350 | 1.50 |
+---------+---------+--------+------+
| Y | A | 150 | 1.75 |
+---------+---------+--------+------+
| X | A | 75 | 2.00 |
+---------+---------+--------+------+
| Y | B | 10 | 1.85 |
+---------+---------+--------+------+
| X | A | 40 | 2.50 |
+---------+---------+--------+------+
Using a traditional join the first row delivers a cost as 600 is a weight listed in 'Table2'
SELECT a.Product
,a.Carrier
,a.Weight
,b.Cost
FROM dbo.table1 a
LEFT JOIN Table2 b ON a.Carrier = b.Carrier AND a.Weight = b.Weight
+---------+---------+--------+------+
| Product | Carrier | Weight | Cost |
+---------+---------+--------+------+
| Z | B | 600 | 1.35 |
+---------+---------+--------+------+
| Z | B | 350 | NULL |
+---------+---------+--------+------+
| Y | A | 150 | NULL |
+---------+---------+--------+------+
| X | A | 75 | NULL |
+---------+---------+--------+------+
| Y | B | 10 | NULL |
+---------+---------+--------+------+
| X | A | 40 | NULL |
+---------+---------+--------+------+
I hope to make changes to the query above to achieved the desired result.
FYI: I am using Microsoft SQL Server 2014.
You should look for the first record that match the conditions:
SELECT a.Product
,a.Carrier
,a.Weight
,(select top 1 Cost from Table2 b
where a.Carrier = b.Carrier AND a.Weight <= b.Weight
ORDER BY b.Carrier, b.Weight ASC) Cost
FROM dbo.table1 a
This should get you started:
with theCost as (
select t1.carrier
, min(t2.weight) thisWeight
from table1 t1 join table2 t2 on t1.carrier = t2.carrier
where whatever
group by t1.carrier
)
select the field you want
from theCost join table2 t2 on theCost.carrier = t2.carrier
and thisWeight = t2.weight
etc