SQL Finding codes - sql

I want to see all my customers who have received a document code Eg. [RF001], however I also want to see that they haven't received a number of document codes E.G [RF002, RF005, RF006, RF009].
All the document codes sit in 1 column, and the customer references are repeated.
Table
Customer_Ref | Doc_Code
CS001 | RF001
CS002 | RF003
CS001 | RF002
Code thats been tried is;
SELECT *
FROM Test_Data
WHERE Doc_Code = RF001

You can use NOT EXISTS to exclude unwanted results :
SELECT *
FROM Table1 tbl
WHERE
Doc_Code='RF001'
AND NOT EXISTS (SELECT 1
FROM Table1 unwanted
WHERE
tbl.Customer_Ref=unwanted.Customer_Ref
AND Doc_Code IN ('RF002', 'RF005', 'RF006', 'RF009'))

SELECT *
FROM Test_Data t1
WHERE t1.Doc_Code = 'RF001'
and not exists (select 1 from Test_Data t2
where t1.Customer_Ref = t2.Customer_Ref
and t2.Doc_Code in ('RF002', 'RF005', 'RF006', 'RF009')

There's several ways to accomplish this.
This is but one way using a join
SELECT Distinct A.CUSTOMER_REF
FROM TABLE A
LEFT JOIN (SELECT DISINTCT CUSTOMER_REF
FROM TABLE B
WHERE B.DOC_CODE IN ('RF002', 'RF005', 'RF006', 'RF009') B
ON A.CUSTOMER_REF = B.CUSTOMER_REF
WHERE A.DOC_CODE = 'RF001'
and B.Customer_REF is null
Another way is to use not exists (I'm sure someone else will do)
Another way is to use a not in (I'm sure someone else will do)
Another way is to use set logic and EXCEPT (Similar to a Union; but subtracts once set from another)
.
SELECT A.Customer_REF FROM table where DOC_CODE = 'RF001'
EXCEPT
SELECT A.Customer_REF FROM Table where DOC_CODE IN ('RF002', 'RF005', 'RF006', 'RF009')

Related

Including multiple columns in NOT IN

I have two tables as below.
Table 1
Book price
A 100
B 200
C 400
D 300
Table 2
Book price
A 100
B 200
C 400
Now I am executing below command as I want only the 4th record to get inserted into table 2. I want to add both the column names before NOT IN. what should I do?
Insert into table2 select * from table1 t1 where t1.book not in (select book from table2);
You can use NOT EXISTS along with matching the presumably primary key columns Book for both of the tables such as
INSERT INTO table2
SELECT *
FROM table1 t1
WHERE NOT EXISTS ( SELECT 0 FROM table2 WHERE Book=t1.Book);
Demo
P.S.: Should be careful about NULL values while using NOT IN operator. Moreover, using NOT IN is mostly less performant than using NOT EXISTS
I tried it and it looks fine to me.
may be you run the script twice so it will show 0 rows created in the second time.
make sure you committed the row.

selecting minimum value depending on other value

Is there any better way for below sql query? Don't want to drop and create temporary table just would like to do it in 1 query.
I am trying to select minimum value for price depending if its order sell where obviously price is higher then in buy and it just shows 0 results when I try it.
DROP TABLE `#temporary_table`;
CREATE TABLE `#temporary_table` (ID int(11),region int(11),nazwa varchar(100),price float,isBuyOrder int,volumeRemain int,locationID int,locationName varchar(100),systemID int,security_status decimal(1,1));
INSERT INTO `#temporary_table` SELECT * FROM hauling WHERE isBuyOrder=0 ORDER BY ID;
SELECT * FROM `#temporary_table`;
SELECT * FROM `#temporary_table` WHERE (ID,price) IN (select ID, MIN(price) from `#temporary_table` group by ID) order by `ID`
UPDATE: when I try nvogel answer and checked profiling thats what I get:
Any chance to optimize this or different working way with 700k rows database?
Try this:
SELECT *
FROM hauling AS h
WHERE isBuyOrder = 0
AND price =
(SELECT MIN(t.price)
FROM hauling AS t
WHERE t.isBuyOrder = 0
AND t.ID = h.ID);
You don't need a temporary table at all. You can basically use your current logic:
SELECT h.*
FROM hauling h
WHERE h.isBuyOrder = 0 AND
(h.id, h.price) IN (SELECT h2.id, MIN(h2.price)
FROM hauling h2
WHERE h2.isBuyOrder = 0
)
ORDER BY h.id
There are many other ways to write similar logic. However, there is no need to rewrite the logic; you just need to include the comparison on isBuyOrder in the subquery.
Note that not all databases support IN with tuples. If your database does not provide such support, then you would need to rewrite the logic.

Sql SubSelect With Multiple Keys

I am trying to copy rows from 1 table to a copy of itself, but only including rows for accounts that exist in a 2nd table.
This works fine with just one key field (account) as below:
insert into newlibr.acpmstpf
select * from oldlibr.acpmstpf as source
where not exists
(select *
from newlibr.acpmstpf as target
where target.acpno = source.acpno
and target.acpbrn = source.acpbrn
and target.acpitm = source.acpitm)
and source.acpno in (select account from accinfo)
In this case I am trying to insert rows from the original table acpmstpf in schema oldlibr to a copy of itself in newlibr, matching rows on the 2 keys account/branch (acpno/acpbrn) and ONLY inserting those rows where the account is in 2nd table accinfo.
What I REALLY want to do is to ONLY insert those rows where the account & branch are in accinfo, because if only 2 branches are in accinfo and there are 100 on acpmstpf, it copies all 100 rows.
I know I could do this with a join, but then I would have to specify all the columns (which could be many - I have this scenario for a few tables).
Is there a way I could do this and still use a sub-select?
You want to replace
and source.acpno in (select account from accinfo)
and look for tuples (account, branch) instead. Many DBMS support this:
and (source.acpno, source.acpbrn) in (select account, branch from accinfo)
For those DBMS that don't, you'd have to resort to EXISTS:
and exists
(
select *
from accinfo
where accinfo.account = source.acpno
and accinfo.branch = source.branch
)
Use exists:
insert into newlibr.acpmstpf
select *
from oldlibr.acpmstpf as source
where not exists (select 1
from newlibr.acpmstpf as target
where target.acpno = source.acpno and
target.acpbrn = source.acpbrn
target.acpitm = source.acpitm
) and
exists (select 1
from accinfo a
where source.acpno = a.accinfo and
source.acpbrn = a.acpbrn
);

Alternative to NOT IN()

I have a table with 14,028 rows from November 2012. I also have a table with 13,959 rows from March 2013. I am using a simple NOT IN() clause to see who has left:
select * from nov_2012 where id not in(select id from mar_2013)
This returned 396 rows and I never thought anything of it, until I went to analyze who left. When I pulled all the ids for the lost members and put them in a temp table (##lost), 32 of them were actually still in the mar_2013 table. I can pull them up when I search for their ids using the following:
select * from mar_2013 where id in(select id from ##lost)
I can't figure out what is going on. I will mention that the id field I created is an IDENTITY column. Could that have any effect on the matching using NOT IN? Is there a better way to check for missing rows between tables? I have also tried:
select a.* from nov_2012 a left join mar_2013 b on b.id = a.id where b.id is NULL
And received the same results.
This is how I created the identity field;
create table id_lookup( dateofcusttable date ,sin int ,sex varchar(12) ,scid int identity(777000,1))
insert into id_lookup (sin, sex) select distinct sin, sex from [Client Raw].dbo.cust20130331 where sin <> 0 order by sin, sex
This is how I added the scid into the march table:
select scid, rowno as custrowno
into scid_20130331
from [Client Raw].dbo.cust20130331 cust
left join id_lookup scid
on scid.sin = cust.sin
and scid.sex = cust.sex
update scid_20130331
set scid = custrowno where scid is NULL --for members who don't have more than one id or sin information is not available
drop table Account_Part2_Current
select a.*, scid
into Account_Part2_Current
from Account_Part1_Current a
left join scid_20130331 b
on b.custrowno = a.rowno_custdmd_cust
I then group all the information by the scid
I would prefer this form (and here's why):
SELECT a.id --, other columns
FROM dbo.nov_2012 AS a
WHERE NOT EXISTS (SELECT 1 FROM dbo.mar_2013 WHERE id = a.id);
However this should still give the same results as what you've tried, so I suspect there is something about the data model that you're not telling us - for example, is mar_2013.id nullable?
this is logically equivalent to not in and is faster than not in.
where yourfield in
(select afield
from somewhere
minus
select
thesamefield
where you want to exclude the record
)
It probably isn't as fast as using where not exists, as per Aaron's answer so you should only use it if not exists does not provide the results you want.

SQL equivalent of IN operator that acts as AND instead of OR?

Rather than describe, I'll simply show what I'm trying to do. 3 tables in 3NF. product_badges is the join table. (The exists sub-query is necessary.)
SELECT * FROM shop_products WHERE EXISTS ( // this line cannot change
SELECT * FROM product_badges as pb
WHERE pb.product_id=shop_products.id
AND pb.badge_id IN (1,2,3,4)
);
Now this will return all the products that have a badge_id of 1 OR 2 OR 3 OR 4. What I want is to get only the products that meet ALL those values. I tried to do pb.badge_id=1 AND pb.badge_id=2 etc but this returns nothing -- which makes sense to me now. I also tried doing multiple queries with an INTERSECT but that resulted in an error. I'm guessing multiple queries is the key but UNION is basically the same as IN and I'm not certain how to use a JOIN in this case.
Please try the following (assuming exactly 4 different required bagde_id's):
SELECT * FROM shop_products WHERE EXISTS ( // this line cannot change
SELECT pb.product_id, count(distinct pb.bagde_id) FROM product_badges as pb
WHERE pb.product_id=shop_products.id
AND pb.badge_id IN (1,2,3,4)
GROUP BY pb.product_id
HAVING count(distinct pb.bagde_id) = 4
);