Select rows that do not have the opposite value in another row - sql

I am trying to write a query that will only select rows that does not have the opposite value. For example if a column (payments) has 2 negative numbers(-11) and 3 positive numbers(11) both the negative numbers would cancel out and one positive number will remain. I may be explaining this wrong. But any help is appreciated.
table :
CREATE TABLE hamzachecks(
ID VARCHAR(2) NOT NULL
,CHECK VARCHAR(10) NOT NULL
,Payment VARCHAR(8) NOT NULL
);
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('1','9549549544','-112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('2','9549549544','-112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('3','9549549544','112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('4','9549549544','112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('5','9549549544','-165.92');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('6','9549549544','225.92');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('7','9549549544','-299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('8','9549549544','-299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('9','9549549544','-299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('10','9549549544','299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('11','9549549544','299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('12','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('13','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('14','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('15','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('16','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('17','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('18','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('19','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('20','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('21','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('22','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('23','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('24','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('25','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('26','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('27','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('28','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('29','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('30','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('31','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('32','9549549544','-1024.22');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('33','9549549544','1024.22');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('34','9549549578','-253.77');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('35','9549549578','253.77');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('36','9549549578','-3332.16');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('37','9549549578','-6664.29');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('38','9549549578','6664.29');

The basic logic is the same as #Marty's, assign a row number to each row in with the same chk/payment combination.
SELECT chk
,id
,Payment
,Row_Number() Over (PARTITION BY chk, Payment ORDER BY Id) AS rn
FROM hamzachecks
order by
chk
-- remove the leading '-'', for numeric data: abs(Payment)
,substring(Payment, charindex('-', Payment)+1, 8000)
,rn
,Payment
It there's a matching row both rows will share the same rn
...
9549549544 3 112.96 1 -- 1st group
9549549544 1 -112.96 1 -- matching value: remove
9549549544 4 112.96 2 -- 2nd group
9549549544 2 -112.96 2 -- matching value: remove
...
9549549544 10 299.3 1 -- 1st group
9549549544 7 -299.3 1 -- matching value: remove
9549549544 11 299.3 2 -- 2nd group
9549549544 8 -299.3 2 -- matching value: remove
9549549544 9 -299.3 3 -- 3rd group, no matching value: keep
...
Now remove the groups with two rows using aggregation:
;WITH cte AS
(
SELECT chk
,id
,Payment
,Row_Number() Over (PARTITION BY chk, Payment ORDER BY Id) AS rn
FROM hamzachecks
)
SELECT
Min(id)
,chk
,max(Payment)
FROM cte
GROUP BY
chk
-- remove the leading '-'', for numeric data: abs(Payment)
,substring(Payment, charindex('-', Payment)+1, 8000)
,rn
HAVING count(*) = 1
See fiddle

I'm going to ignore possible considerations for why you want to do this particular job in this particular way.
So basically: the following SQL will give you the result you are asking for...
;with b (id, payment, rn) as
(select id, payment, row_number() over (partition by Payment order by Id) as rn
from hamzachecks
where payment < 0),
a (id, payment, rn) as
(select id, payment, row_number() over (partition by Payment order by Id) as rn
from hamzachecks
where payment > 0),
matched_ids (id1,id2) as
(select a.id id1, b.id id2
from a
join b on a.payment = -b.payment and a.rn = b.rn)
select *
from hamzachecks
where id not in (select id1 from matched_ids union select id2 from matched_ids)
order by id
This needs a change in your schema: define the Payment column as MONEY (or other number type) instead of VARCHAR(8).
This would be a starting point for further work, as this solution is ignoring the CHECK column.
Explanation: first we split the source table into two groups: a - above zero (payment > 0) and b - below zero (payment < 0). Both of those groups will get another column row_number() ... that get gets each row an "rowID" - based on the position in the particular group (partition by Payment) that basically says: "This is n-th row with the same Payment". The join part then matches all rows that have the same (but opposite) Payment in the other group and also checks the rowID - we now know all rows that have an opposite value. So the main select just ignores those rows and return only those that don't have an opposite value.

Related

Oracle sql to query last N rows as rest of dividing by fixed value

Suppose I have something like this:
CREATE TABLE "PIPPO" ("COLUMN1" number);
INSERT INTO PIPPO (COLUMN1) VALUES (1);
INSERT INTO PIPPO (COLUMN1) VALUES (2);
INSERT INTO PIPPO (COLUMN1) VALUES (3);
INSERT INTO PIPPO (COLUMN1) VALUES (4);
INSERT INTO PIPPO (COLUMN1) VALUES (5);
INSERT INTO PIPPO (COLUMN1) VALUES (6);
INSERT INTO PIPPO (COLUMN1) VALUES (7);
INSERT INTO PIPPO (COLUMN1) VALUES (8);
Is there an "easy" way to run a SELECT query on the table to get the last N rows, where N is the rest of divide by 3?
For example for this case I would like o retrieve:
7
8
Imagine to insert another record
INSERT INTO PIPPO (COLUMN1) VALUES (9);
In this case, I would like to retrieve:
7
8
9
Imagine inserting yet another record:
INSERT INTO PIPPO (COLUMN1) VALUES (10);
Now in this case I would like to retrieve just:
10
Any ideas?
Let the innermost subquery return the number of rows to return. Use FETCH FIRST to get that number of rows:
select * from
(
select * from PIPPO
order by COLUMN1 desc
fetch first (select mod(count(*)-1, 3) + 1 from PIPPO) rows only
) dt
order by COLUMN1
You can use FETCH FIRST n ROWS ONLY and use a sub-query to get the n value:
SELECT *
FROM pippo
ORDER BY column1 DESC
FETCH FIRST (SELECT DECODE(MOD(COUNT(*), 3), 0, 3, 1, 1, 2, 2) FROM pippo) ROWS ONLY
or you can use analytic functions:
SELECT column1
FROM (
SELECT column1,
ROW_NUMBER() OVER (ORDER BY column1 DESC) AS rn,
MOD(COUNT(*) OVER (), 3) AS rem
FROM pippo p
)
WHERE rn <= DECODE(rem, 0, 3, rem);
fiddle

SQL (PLSQL) number a set of rows

I attended an interview recently and the interviewer asked me number the occurrences of 'A', 'B', 'C' and so on. To put in table and columns - there is a table tab with column as col. The values in col is 'A', 'B', 'C' etc.
create table tab226 (col varchar2(3) );
insert into tab226 VALUES ('A');
insert into tab226 VALUES ('B');
insert into tab226 VALUES ('C');
insert into tab226 VALUES ('B');
insert into tab226 VALUES ('A');
insert into tab226 VALUES ('C');
insert into tab226 VALUES ('C');
insert into tab226 VALUES ('A');
insert into tab226 VALUES ('B');
The expected output is :
Interviewer told me I can use SQL or PLSQL to achieve it. I thought about it for almost 10 mins but couldn't come up with a plan let alone the solution. Does anyone know if this can be achieved in Oracle SQL or PLSQL?
Doesn't make much sense to me, but - would this do?
SQL> select col,
2 count(*) over (partition by col order by rowid) exp_output
3 from tab226
4 order by rowid;
COL EXP_OUTPUT
--- ----------
A 1
B 1
C 1
B 2
A 2
C 2
C 3
A 3
B 3
9 rows selected.
SQL>
As written, you cannot accomplish -- consistently -- what they are asking for. The problem is that SQL tables represent unordered sets. There is no way to run a query on the original data and preserve the ordering.
However, the final column appears to simply be an enumeration, so you can use row_number() for that:
select col, row_number() over (partition by col order by NULL)
from tab226;
But if you have an ordering column -- say id in this example -- then you would do:
select col, row_number() over (partition by col order by NULL)
from tab226;
Here is a db<>fiddle.

Concat single column values without supporting column(ex:- ID) in SQL Server 2014

I have a single column
Value
A
B
C
NULL
NULL
F
R
D
NULL
T
R
NULL
Expected Output:
value
ABC
FRD
TR
Aim is to group column values until next NULL and so on..
CREATE TABLE DUMMY1(VALUE VARCHAR(50))
INSERT INTO DUMMY1(VALUE) VALUES('A')
INSERT INTO DUMMY1(VALUE) VALUES('B')
INSERT INTO DUMMY1(VALUE) VALUES('C')
INSERT INTO DUMMY1(VALUE) VALUES(NULL)
INSERT INTO DUMMY1(VALUE) VALUES(NULL)
INSERT INTO DUMMY1(VALUE) VALUES('F')
INSERT INTO DUMMY1(VALUE) VALUES('R')
INSERT INTO DUMMY1(VALUE) VALUES('D')
INSERT INTO DUMMY1(VALUE) VALUES(NULL)
INSERT INTO DUMMY1(VALUE) VALUES('T')
INSERT INTO DUMMY1(VALUE) VALUES('R')
INSERT INTO DUMMY1(VALUE) VALUES(NULL)
SQL tables represent unordered sets. Your question makes no sense, unless I assume that that rows are ordered. This requires a separate column for the ordering.
Let me assume that you have such a column, which I will call id. Then, you can use a cumulative count to assign the groups and then aggregate:
SELECT STRING_AGG(value, '') WITHIN GROUP (ORDER BY id)
FROM (SELECT d.*,
SUM(CASE WHEN VALUE IS NULL THEN 1 ELSE 0 END) OVER (ORDER BY id) as grp
FROM DUMMY1 d
) d
WHERE value IS NOT NULL
GROUP BY grp;
Here is a db<>fiddle.
You can try below:
SELECT value from STRING_SPLIT
(
(select SUBSTRING(
(
SELECT ''+ isnull(Value,',')
FROM DUMMY1
FOR XML PATH('')
), 1 , 9999)),
',') as SS
where SS.value <> ''

Finding trouble in generating the list of all Students who belongs to the major named as “CS” but not to major named as “EE”

S_id int Primary Key,
S_name varchar(100),
Gpa float ,
Size_hs int
)
Create table Apply (
s_id int ,
C_name varchar(100),
Major varchar(10),
Decision varchar(2)
)
insert into Students values (123,'Amy',3.9,1000)
insert into Students values (234,'Bob',3.6,1500)
insert into Students values (345,'Craig',3.5,500)
insert into Students values (456,'Doug',3.9,1000)
insert into Students values (567,'Edward',2.9,2000)
insert into Students values (678,'Fay',3.8,200)
insert into Students values (789,'Gray',3.4,800)
insert into Students values (987,'Helen',3.7,800)
insert into Students values (876,'Irene',3.9,400)
insert into Students values (765,'Jay',2.9,1500)
insert into Students values (654,'Amy',3.9,1000)
insert into Students values (543,'Craig',3.4,2000)
insert into Apply values (123,'NJIT','CS','Y')
insert into Apply values (123,'NJIT','EE','N')
insert into Apply values (123,'Stoony Brook','CS','Y')
insert into Apply values (123,'Cornell','EE','Y')
insert into Apply values (234,'Stoony Brook','Bio','N')
insert into Apply values (345,'WPI','Bio-Eng','Y')
insert into Apply values (345,'Cornell','Bio-Eng','N')
insert into Apply values (345,'Cornell','CS','Y')
insert into Apply values (345,'Cornell','EE','N')
insert into Apply values (678,'NJIT','Hist','Y')
insert into Apply values (987,'NJIT','CS','Y')
insert into Apply values (987,'Stoony Brook','CS','Y')
insert into Apply values (876,'NJIT','Bio','N')
insert into Apply values (876,'WPI','Marine-Bio','Y')
insert into Apply values (876,'WPI','Hist','N')
insert into Apply values (765,'NJIT','Hist','Y')
insert into Apply values (765,'Cornell','Hist','N')
insert into Apply values (765,'Cornell','Psych','Y')
insert into Apply values (543,'WPI','CS','N')
Create table Students(
S_id int Primary Key,
S_name varchar(100),
Gpa float ,
Size_hs int
)
Create table Apply (
s_id int ,
C_name varchar(100),
Major varchar(10),
Decision varchar(2)
)
insert into Students values (123,'Amy',3.9,1000)
insert into Students values (234,'Bob',3.6,1500)
insert into Students values (345,'Craig',3.5,500)
insert into Students values (456,'Doug',3.9,1000)
insert into Students values (567,'Edward',2.9,2000)
insert into Students values (678,'Fay',3.8,200)
insert into Students values (789,'Gray',3.4,800)
insert into Students values (987,'Helen',3.7,800)
insert into Students values (876,'Irene',3.9,400)
insert into Students values (765,'Jay',2.9,1500)
insert into Students values (654,'Amy',3.9,1000)
insert into Students values (543,'Craig',3.4,2000)
insert into Apply values (123,'NJIT','CS','Y')
insert into Apply values (123,'NJIT','EE','N')
insert into Apply values (123,'Stoony Brook','CS','Y')
insert into Apply values (123,'Cornell','EE','Y')
insert into Apply values (234,'Stoony Brook','Bio','N')
insert into Apply values (345,'WPI','Bio-Eng','Y')
insert into Apply values (345,'Cornell','Bio-Eng','N')
insert into Apply values (345,'Cornell','CS','Y')
insert into Apply values (345,'Cornell','EE','N')
insert into Apply values (678,'NJIT','Hist','Y')
insert into Apply values (987,'NJIT','CS','Y')
insert into Apply values (987,'Stoony Brook','CS','Y')
insert into Apply values (876,'NJIT','Bio','N')
insert into Apply values (876,'WPI','Marine-Bio','Y')
insert into Apply values (876,'WPI','Hist','N')
insert into Apply values (765,'NJIT','Hist','Y')
insert into Apply values (765,'Cornell','Hist','N')
insert into Apply values (765,'Cornell','Psych','Y')
insert into Apply values (543,'WPI','CS','N')
Basically I have to find the list of students id who belongs to major named as 'Cs' but not to the major 'EE'. I tried it by myself but it is not working properly.
Here is the code below:
select * from students
where s_id in (
select s_id
from apply
where major='CS' and Major!='EE'
group by s_id
)```
I would use exists and not exists:
select s.*
from students s
where
exists (select 1 from applies a where a.s_id = s._id and a.major = 'CS')
and not exists (select 1 from applies a where a.s_id = s._id and a.major = 'EE')
With an index on applies(s_id, major), this should be an efficient option.
Another approach is to join, aggregate, and filter with a having clause. This requires listing the columns that you want to show in both the select and group by clauses:
select s.s_id, s.s_name
from students s
inner join applies a on a.s_id = s.s_id
where a.major in ('CS', 'EE')
group by s.s_id, s.s_name
having
max(case when a.major = 'CS' then 1 else 0 end) = 1
and max(case when a.major = 'EE' then 1 else 0 end) = 0
Note: apply is a reserved word in a number a databases (SQL Serer, Oracle, ...), hence not a good choice for a table name. I renamed it to applies in the queries.

How to get the latest last 2 inserted records from the table

CREATE TABLE `testskm`(
`mem_id` NUMBER(5) NOT NULL,
`mem_sal` NUMBER(5) NOT NULL);
insert into `testskm` values (1,100);
insert into `testskm` values (1,200);
insert into `testskm` values (2,350);
insert into testskm values (2,150);
insert into testskm values (3,12);
insert into testskm values (1,300);
insert into testskm values (2,50);
insert into testskm values (3,13);
insert into testskm values (3,14);
insert into testskm values (3,15);
i have insert statements for mem_id 1,2, & 3. I want to get the last 2 inserted records in the table for all mem_id.
I have the tried the below code , but its giving me only records based on the mem_sal as i used the order by..
select * from(
select
mem_id, mem_sal,
--max(sysdate) over (partition by mem_id) latest,
rank() over( partition BY mem_id order by mem_sal ) RISK_ORDER
from testskm)
where RISK_ORDER <= 2
i want output of these inserted records:
insert into `testskm` values (1,200);
insert into `testskm` values (1,300);
insert into `testskm` values (2,150);
insert into `testskm` values (2,50);
insert into `testskm` values (3,14);
insert into `testskm` values (3,15);
SQL tables represent unordered sets. There is no "last two rows" unless a column specifies the ordering. Your table has no such column.
You can define one. In MySQL, this looks like:
create table `testskm` (
testskm_id int auto_increment primary key,
`mem_id` int NOT NULL,
`mem_sal` int NOT NULL
);
Then you can use row_number():
select ts.*
from (select ts.*, row_number() over (partition by mem_id order by testskm_id desc) as seqnum
from testskm ts
) ts
where seqnum <= 2;
Here is a db<>fiddle.