Oracle ALL on SUBQUERY - sql

I have simple question about Corelated Sub-Query:
The tables and data is as follows :
CREATE TABLE AUTHORS
(
NAME VARCHAR2(10 BYTE)
);
Insert into AUTHORS Values ('john');
Insert into AUTHORS Values ('bill');
Insert into AUTHORS Values ('dave');
CREATE TABLE BOOKS
(
NAME VARCHAR2(10 BYTE),
PRICE INTEGER
);
Insert into BOOKS Values ('john', 101);
Insert into BOOKS Values ('john', 200);
Insert into BOOKS Values ('john', 300);
insert into books values ('bill', 200 );
Insert into BOOKS Values ('bill', 10);
Insert into BOOKS Values ('dave', 5);
COMMIT;
Question: here 'john' has all books with price > 100.
But when I use following query, only one row is returned:
select * from
authors a
where 100 < all( select price from books b where a.nAME = b.NAME );

If you want to get all the matched records with books table with given condition then below query will work
select * from
authors a join books c on a.name=c.name
where 100 < all( select price from books b where a.nAME = b.NAME );

If you want all the books for names, where the minimum price is greater than 100, I would simply do:
select b.*
from (select b.*, min(b.price) over (partition by b.name) as minprice
from books b
) b
where minprice > 100;
If you only wanted the name, I would use aggregation:
select b.name
from books b
group by b.name
having min(b.price) > 100;
Both of these seem simpler than your approach.

If you are asking to return all books for John where price is > 100 then this should do it.
SELECT *
FROM authors a
INNER JOIN books b ON a.name = b.name
WHERE a.name = 'john' AND b.price > 100;

Related

SQL join on N-M realtion with AND condition

I have TableA and TableB and a relation TableAB for a many to many (N-M) relation
TableA JOIN TableAB JOIN TableB
How can I add an AND-condition to check if TableAB has entries for multiple conditions in TableB
Maybe a stupid but simple example:
TableA = firstnames
TableB = lastnames
I want all firstnames that are used in combination with lastname "Smith", "Black" AND "Taylor"
I thought about using
WHERE lastname = "Smith" or lastname = "Black" or lastname = "Taylor"
GROUP BY firstname and check if count(*) = 3
But this seems to be not the right way, as lastname = "Smith" or lastname = "Smith" or lastname = "Smith" should not return an empty table.
And it should be easy to extend, as the filter is user input, and don't know how many conditions I get.
Bonus question: I use Sequelize with a Many-to-Many Association, not sure how to put this in here
EDIT:
Example:
TableA
id
firstname
1
Tom
2
Alice
3
Bob
TableB
id
lastname
1
Smith
2
Black
3
Taylor
TableAB
id
id_firstname
id_lastname
1
1
1
2
2
1
3
2
2
2
3
3
Meaning we have Tom Smith, Alice Smith, Alice Black, Bob Taylor
Query: Give me all firstnames with lastname Smith AND Black
Result: Alice
as always: there are different ways to achieve that. One is a multiple join of the NM table. Check the select on the end of the script (written for MS SQL Server). The rest of the script is just to create the sample data.
create table TableA (
ID int not null,
Firstname nvarchar(255) not null);
create table TableB (
ID int not null,
Lastname nvarchar(255) not null);
create table TableAB(
IDA int not null,
IDB int not null);
GO
insert into TableA values (1, 'Tom');
insert into TableA values (2, 'Alice');
insert into TableA values (3, 'Bob');
insert into TableB values (1, 'Smith');
insert into TableB values (2, 'Black');
insert into TableB values (3, 'Taylor');
insert into TableAB values (1, 1);
insert into TableAB values (2, 1);
insert into TableAB values (2, 2);
insert into TableAB values (3, 3);
GO
select A.Firstname
from TableA A
inner join TableAB AB1 on A.ID = AB1.IDA
inner join TableB B1 on B1.ID = AB1.IDB
inner join TableAB AB2 on A.ID = AB2.IDA
inner join TableB B2 on B2.ID = AB2.IDB
where B1.Lastname = 'Smith'
and B2.Lastname = 'Black'
other way would be a sub-select to return all combinations of Lastname + Firstname and then filter for the requested Lastname and count the occurance:
select Firstname
from (
select distinct A.Firstname, B.Lastname
from TableA A
inner join TableAB AB on A.ID = AB.IDA
inner join TableB B on B.ID = AB.IDB
) t
where Lastname in ('Smith', 'Black')
group by Firstname
having count(*) = 2
Both ways will return Alice as a result only.
Thank you for your reply. The second one is similar to what I thought would work:
select A.Firstname, B.Lastname
from TableA A
inner join TableAB AB on A.ID = AB.IDA
inner join TableB B on B.ID = AB.IDB AND Lastname in ('Smith', 'Black')
group by A.Firstname having count(*) = 2
But it must be checked if all user inputs of lastnames are distinct as "in ('Smith', 'Smith')" will not work with count = 2
That's why I thought there exists better solutions. The first suggestion would be fine with this, but its a mess to implement this automated with prepared statements in sequelize.

Using an Inner Join and returning only 1 record per match on the left

I don't know why I'm not able to figure this out ATM. Here's a very contrived example.
create table Dog (
[Name] varchar(10),
[BreedId] int
);
create table Breed (
[BreedId] int,
[Name] varchar(10)
);
insert into DOG (name, breedid)
VALUES ('Mix', 1),
('Mix2', 2),
('Mix2', 3);
insert into breed(breedid, name)
VALUES
(1, 'BullDog'),
(1, 'Poodle'),
(2, 'BullDog'),
(2, 'Poodle'),
(4, 'Poodle');
The following produces two rows because the join table has two matching values, I just want one value as long as there is a matching record on the right.
select d.*
from dog d
inner join breed b on d.breedid = b.breedid
results in:
Mix 1
Mix 1
Mix 2
Mix 2
Is there a way to do this in the join clause / without doing a select in the where clase or other options like group by.
select d.*
from dog d
where d.breedid in (select breedid from breed)
Try "select distinct". You can also group by the columns that are duplicates and take the max() of columns that have unique values.
Edit: how to filter by breed.breedid without using a where clause.
select d.*
From dog as d
Inner join (select distinct breedid from breed) as b on b.breedid = d.breedid
I would use exists if you only want columns from dog:
select d.*
from dog d
where exists (select 1 from breed b where d.breedid = b.breedid);

Oracle SQL - Comparing Rows

I have a problem I'm working on with Oracle SQL that goes something like this.
TABLE
PurchaseID CustID Location
----1------------1-----------A
----2------------1-----------A
----3------------2-----------A
----4------------2-----------B
----5------------2-----------A
----6------------3-----------B
----7------------3-----------B
I'm interested in querying the Table to return all instances where the same customer makes a purchase in different locations. So, for the table above, I would want:
OUTPUT
PurchaseID CustID Location
----3------------2-----------A
----4------------2-----------B
----5------------2-----------A
Any ideas on how to accomplish this? I haven't been able to think of how to do it, and most of my ideas seem like they would be pretty clunky. The database I'm using has 1MM+ records, so I don't want it to run too slowly.
Any help would be appreciated. Thanks!
SELECT *
FROM YourTable T
WHERE CustId IN (SELECT CustId
FROM YourTable
GROUP BY CustId
HAVING MIN(Location) <> MAX(Location))
You should be able to use something similar to the following:
select purchaseid, custid, location
from yourtable
where custid in (select custid
from yourtable
group by custid
having count(distinct location) >1);
See SQL Fiddle with Demo.
The subquery in the WHERE clause is returning all custids that have a total number of distinct locations that are greater than 1.
In English:
Select a row if another row exists with the same customer and a different location.
In SQL:
SELECT *
FROM atable t
WHERE EXISTS (
SELECT *
FROM atable
WHERE CustID = t.CustID
AND Location <> t.Location
);
Here's one approach using a sub-query
SELECT T1.PurchaseID
,T1.CustID
,T1.Location
FROM YourTable T1
INNER JOIN
(SELECT T2.CustID
,COUNT (DISTINCT T2.Location )
FROM YourTable T1
GROUP BY
T2.CustID
HAVING COUNT (DISTINCT T2.Location )>1
) SQ
ON SQ.CustID = T1.CustID
This should only require one full table scan.
create table test (PurchaseID number, CustID number, Location varchar2(1));
insert into test values (1,1,'A');
insert into test values (2,1,'A');
insert into test values (3,2,'A');
insert into test values (4,2,'B');
insert into test values (5,2,'A');
insert into test values (6,3,'B');
insert into test values (7,3,'A');
with repeatCustDiffLocations as (
select PurchaseID, custid, location, dense_rank () over (partition by custid order by location) r
from test)
select b.*
from repeatCustDiffLocations a, repeatCustDiffLocations b
where a.r > 1
and a.custid = b.custid;
This makes most sense to me as I was trying to return the rows with the same values throughout the table, specifically for two columns as shown in this stackoverflow answer here.
The answer to your problem in this format is:
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.LOCATION <> b.LOCATION;
However, the solution to a problem such as mine with two columns having matching values in multiple rows (2 in this instance, would yield no results because all PurchaseID's are unique):
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.PURCHASEID = b.PURCHASEID AND
a.LOCATION <> b.LOCATION;
Although, this wouldn't return the correct results based on the what needs to be queried, it shows that the query logic works
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.PURCHASEID <> b.PURCHASEID AND
a.LOCATION = b.LOCATION;
If anyone wants to try in Oracle here is the table and values to insert:
CREATE TABLE TEST (
PurchaseID integer,
CustomerID integer,
Location varchar(1));
INSERT ALL
INTO TEST VALUES (1, 1, 'A')
INTO TEST VALUES (2, 1, 'A')
INTO TEST VALUES (3, 2, 'A')
INTO TEST VALUES (4, 2, 'B')
INTO TEST VALUES (5, 2, 'A')
INTO TEST VALUES (6, 3, 'B')
INTO TEST VALUES (7, 3, 'B')
SELECT * FROM DUAL;

Join to only the "latest" record with t-sql

I've got two tables. Table "B" has a one to many relationship with Table "A", which means that there will be many records in table "B" for one record in table "A".
The records in table "B" are mainly differentiated by a date, I need to produce a resultset that includes the record in table "A" joined with only the latest record in table "B". For illustration purpose, here's a sample schema:
Table A
-------
ID
Table B
-------
ID
TableAID
RowDate
I'm having trouble formulating the query to give me the resultset I'm looking for any help would be greatly appreciated.
SELECT *
FROM tableA A
OUTER APPLY (SELECT TOP 1 *
FROM tableB B
WHERE A.ID = B.TableAID
ORDER BY B.RowDate DESC) as B
select a.*, bm.MaxRowDate
from (
select TableAID, max(RowDate) as MaxRowDate
from TableB
group by TableAID
) bm
inner join TableA a on bm.TableAID = a.ID
If you need more columns from TableB, do this:
select a.*, b.* --use explicit columns rather than * here
from (
select TableAID, max(RowDate) as MaxRowDate
from TableB
group by TableAID
) bm
inner join TableB b on bm.TableAID = b.TableAID
and bm.MaxRowDate = b.RowDate
inner join TableA a on bm.TableAID = a.ID
table B join is optional: it depends if there are other columns you want
SELECT
*
FROM
tableA A
JOIN
tableB B ON A.ID = B.TableAID
JOIN
(
SELECT Max(RowDate) AS MaxRowDate, TableAID
FROM tableB
GROUP BY TableAID
) foo ON B.TableAID = foo.TableAID AND B.RowDate= foo.MaxRowDate
With ABDateMap AS (
SELECT Max(RowDate) AS LastDate, TableAID FROM TableB GROUP BY TableAID
),
LatestBRow As (
SELECT MAX(ID) AS ID, TableAID FROM ABDateMap INNER JOIN TableB ON b.TableAID=a.ID AND b.RowDate = LastDate GROUP BY TableAID
)
SELECT columns
FROM TableA a
INNER JOIN LatestBRow m ON m.TableAID=a.ID
INNER JOIN TableB b on b.ID = m.ID
Just for the clarity's sake and to benefit those who will stumble upon this ancient question. The accepted answer would return duplicate rows if there are duplicate RowDate in Table B. A safer and more efficient way would be to utilize ROW_NUMBER():
Select a.*, b.* -- Use explicit column list rather than * here
From [Table A] a
Inner Join ( -- Use Left Join if the records missing from Table B are still required
Select *,
ROW_NUMBER() OVER (PARTITION BY TableAID ORDER BY RowDate DESC) As _RowNum
From [Table B]
) b
On b.TableAID = a.ID
Where b._RowNum = 1
Try using this:
BEGIN
DECLARE #TB1 AS TABLE (ID INT, NAME VARCHAR(30) )
DECLARE #TB2 AS TABLE (ID INT, ID_TB1 INT, PRICE DECIMAL(18,2))
INSERT INTO #TB1 (ID, NAME) VALUES (1, 'PRODUCT X')
INSERT INTO #TB1 (ID, NAME) VALUES (2, 'PRODUCT Y')
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (1, 1, 3.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (2, 1, 4.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (3, 1, 5.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (1, 2, 0.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (2, 2, 1.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (3, 2, 2.99)
SELECT A.ID, A.NAME, B.PRICE
FROM #TB1 A
INNER JOIN #TB2 B ON A.ID = B.ID_TB1 AND B.ID = (SELECT MAX(ID) FROM #TB2 WHERE ID_TB1 = A.ID)
END
This will fetch the latest record with JOIN. I think this will help someone
SELECT cmp.*, lr_entry.lr_no FROM
(SELECT * FROM lr_entry ORDER BY id DESC LIMIT 1)
lr_entry JOIN companies as cmp ON cmp.id = lr_entry.company_id

sql - getting the id from a row based on a group by

Table A
tableAID
tableBID
grade
Table B
tableBID
name
description
Table A links to Table b from the tableBID found in both tables.
If I want to find the row in Table A, which has the highest grade, for each row in Table B, I would write my query like this:
select max(grade) from TableA group by tableBID
However, I don't just want the grade, I want the grade plus id of that row.
You could try something like
SELECT a.*
FROM TableA a INNER JOIN
(
SELECT tableBID,
MAX(grade) MaxGrade
FROM TableA
GROUP BY tableBID
) B ON a.tableBID = B.tableBID AND a.grade = B.MaxGrade
Using the Sql Server 2005 ROW_NUMBER function you could also try
SELECT tableAID,
tableBID,
grade
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY tableBID ORDER BY grade DESC) RowNum
FROM #TableA
) A
WHERE a.RowNum = 1
Assuming you will only have one Max() (HUGE ASSUMPTION) you can do min(tableaid) where grade = (select max(grade) etc....)
or if you want all matching ids
SELECT TableAID, Grade
FROM TableA INNER JOIN (SELECT tableBID,
MAX(grade) Grade
FROM TableA
GROUP BY tableBID) MaxGrade
ON TableA.Grade = MaxGrade.Grade
Just use a little self-referencing NOT EXISTS clause:
DECLARE #tableA TABLE (tableAID int IDENTITY(1,1), tableBID int, grade int)
INSERT INTO #tableA(tableBID, grade) VALUES(10, 1)
INSERT INTO #tableA(tableBID, grade) VALUES(10, 3)
INSERT INTO #tableA(tableBID, grade) VALUES(20, 4)
INSERT INTO #tableA(tableBID, grade) VALUES(20, 8)
INSERT INTO #tableA(tableBID, grade) VALUES(30, 10)
INSERT INTO #tableA(tableBID, grade) VALUES(30, 6)
SELECT tableAID, grade
FROM #tableA ta
WHERE NOT EXISTS (SELECT 1 FROM #tableA WHERE tableBID = ta.tableBID AND grade > ta.grade)