How records can be retrieved based on three conditions? - sql

I'm new to HQL. I need to fetch all the records from table A based on the following 2 condition using HQL/SQL query:
Person ID which satisfies both these conditions "(Music < 100) and (Dance != Normal)" (in Table B) and whose Place and Country is A and AAA (in Table C).
Tables below:
[
[
[
How can I fetch these records based on this three conditions. Could someone help me.
The output should be
Record having ID as 100 in Table A since it has place and value as 'A' and 'AA'. And it also has both Music and Dance skills with Music value greater than 100 and Dance value is not like 'Normal'

select
*
from a
inner join b as music on a.id = music.person_id and music.skills = 'Music'
inner join b as dance on a.id = dance.person_id and dance.skills = 'Dance'
inner join c on a.id = c.id
where c.place = 'A' and c.country = 'AAA'
and music.score < '100'
and dance.score <> 'Normal'
You will have a problem attempting to use less than on the column "score" which is text and not numeric, see test cases below
CREATE TABLE mytable(
Value VARCHAR(6) NOT NULL PRIMARY KEY
);
INSERT INTO mytable(Value) VALUES ('100');
INSERT INTO mytable(Value) VALUES ('a');
INSERT INTO mytable(Value) VALUES ('aa');
INSERT INTO mytable(Value) VALUES ('bbb');
INSERT INTO mytable(Value) VALUES ('cccc');
INSERT INTO mytable(Value) VALUES ('99');
INSERT INTO mytable(Value) VALUES ('9');
INSERT INTO mytable(Value) VALUES ('1');
INSERT INTO mytable(Value) VALUES ('19');
select
*
from mytable where value < '100'
| value |
| :---- |
| 1 |
select
*
from mytable where value > '100'
| value |
| :---- |
| a |
| aa |
| bbb |
| cccc |
| 99 |
| 9 |
| 19 |
select
*
from mytable where cast(value as integer) > 100
ERROR: invalid input syntax for integer: "a"
db<>fiddle here

Related

How to the write SQL to show the data in my case in Oracle?

I have a table like this -
create table tbl1
(
id number,
role number
);
insert into tbl1 values (1, 1);
insert into tbl1 values (2, 3);
insert into tbl1 values (1, 3);
create table tbl2
(
role number,
meaning varchar(50)
);
insert into tbl2 values (1, 'changing data');
insert into tbl2 values (2, 'move file');
insert into tbl2 values (3, 'dance');
I want the sql result like the following -
id role_meaning is_permitted
1 changing data yes
1 move file no
1 dance yes
2 changing data no
2 move file no
2 dance yes
Please help how can I do this? I have tried several methods but not sure how to do this.
You can use partitioned outer join here.
SQL Fiddle
Query 1:
select tbl1.id,
tbl2.meaning,
case when tbl1.role is NULL then 'no' else 'yes' end is_permitted
from tbl1
partition by (id) right outer join tbl2
on tbl1.role = tbl2.role
order by tbl1.id, tbl2.role
Results:
| ID | MEANING | IS_PERMITTED |
|----|---------------|--------------|
| 1 | changing data | yes |
| 1 | move file | no |
| 1 | dance | yes |
| 2 | changing data | no |
| 2 | move file | no |
| 2 | dance | yes |

conditional joining in oracle

Conditional joining statement : -
High level Description :-
Join table based on two columns if combination is not present then join on one table -
Detailed Table -
create table tab1
(tab1_col1 number not null,
tab1_col2 number null,
tab1_col3 varchar(10));
Lookup Table
create table lkp1
(lkp_col1 number not null,
lkp_col2 number not null,
lkp_col3 number not null,
lkp_col4 varchar(10));
Insert Statement -
tab1
insert into tab1 values (10,101,'A');
insert into tab1 values (12,101,'B');
insert into tab1 values (11,102,'C');
insert into tab1 values (13,103,'B');
insert into tab1 values (14,104,'C');
insert into tab1 values (15,108,'A');
insert into tab1 values (16,102,'D');
Lookup Table
lkp1
insert into lkp1 values (10,101,50,'PICK');
insert into lkp1 values (10,101,50,'PICK');
insert into lkp1 values (11,102,100,'SKIP');
insert into lkp1 values (11,110,50,'PICK');
insert into lkp1 values (13,103,40,'PICK');
insert into lkp1 values (13,103,60,'PICK');
insert into lkp1 values (14,199,100,'PICK');
insert into lkp1 values (15,115,80,'PICK');
insert into lkp1 values (15,115,20,'PICK');
Requirement was -
Join table based on tab1_col1=lkp_col1
and
tab1_col2=lkp_col2
Filter out lookup table data lkp_col4=SKIP
If record not present in lookup table for then give default value(99.99).
(All records from tab1 table should be selected).
I built this query and it was working fine.
SELECT tab1_col1, tab1_col2, NVL (lkp_col3, '99.99') "LKP_COL3"
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1(+) AND tab1_col2 = lkp_col2(+)
Now requirement changed
First check if
tab1_col1=lkp_col1
and
tab1_col2=lkp_col2
If lookup table is not having data for this combination
then check again with
tab1_col1=lkp_col1
If this is not also available then give dafault value.
Database - Oracle 10g
What I have tried so far
After lot of trail and error I m able to get the output. However, is there a better or simple way to use this ?
SELECT tab1_col1, tab1_col2, LKP_COL3
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1 AND tab1_col2 = lkp_col2
union all
SELECT tab1_col1, tab1_col2, LKP_COL3
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1(+)
AND
LKP_COL1|| '-' || LKP_COL2 not in( SELECT tab1_col1|| '-' || tab1_col2
FROM tab1, lkp1 lkp
WHERE tab1_col1 = lkp_col1 AND tab1_col2 = lkp_col2)
order by 1
The following result:
| TAB1_COL1 | TAB1_COL2 | LKP_COL3 |
|-----------|-----------|----------|
| 10 | 101 | 50 |
| 11 | 102 | 50 |
| 12 | 101 | 99.99 |
| 13 | 103 | 40 |
| 13 | 103 | 60 |
| 14 | 104 | 100 |
| 15 | 108 | 20 |
| 15 | 108 | 80 |
| 16 | 102 | 99.99 |
was produced by this query:
SELECT DISTINCT
tab1.tab1_col1
, tab1.tab1_col2
, COALESCE(lkp1.lkp_col3, lkp2.lkp_col3, 99.99) "LKP_COL3"
FROM tab1
LEFT JOIN lkp1
ON tab1.tab1_col1 = lkp1.lkp_col1
AND tab1.tab1_col2 = lkp1.lkp_col2
AND lkp1.lkp_col4 = 'PICK'
LEFT JOIN lkp1 lkp2
ON tab1.tab1_col1 = lkp2.lkp_col1
AND lkp2.lkp_col4 = 'PICK'
ORDER BY
tab1.tab1_col1
, tab1.tab1_col2
;
DISTINCT was added because the second left (outer) join produces unwanted repetition in the output.
refer to this sqlfiddle

SQL - Compare Table1.items (Ntext) to Table2.item (Varchar)

I'm working on SQL Server 2012.
I would like to split the different items from the Table1 to compare with a specific column of the Table2.
Table1 have a row like that :
| id | items |
| 1 | aaa;ery;sha;cbre;dezrzyg; |
| 2 | aaa;ery;sha;cbre;dezrzyg; | // Could be the same items than another row
| 3 | dg;e3ry;sd6ha;cb8re;48dz; |
| 4 | e5zeza;48;dz;46az;12BREd; |
| ... | ... |
| 10 | aaa | // Currently match because the request compare the whole cell
items is a string (ntext in the db) and the string never contain spaces.
Table2 have a row like that :
| id | item |
| 1 | aaa | // match
| 2 | AAA | // match
| 3 | aaa52 | // doesn't match
| 4 | 2aaa2 | // doesn't match
| ... | ... |
item also is a string (nvarchar in the db) and the string never contain spaces.
Here is my current SQL request :
SELECT * FROM Table1 t1
INNER JOIN Table2 t2 ON t1.items = t2.item
How could I solve my problem ?
Should I split a string then compare each Table1.items to Table2.item ?
Is there something in SQL to resolve it easily ?
Is there something in SQL to resolve it easily ?
No but you can creatively use like. Indexes can not help you with performance when you do something like this.
select *
from Table1 as T1
inner join Table2 as T2
on ';'+cast(T1.items as nvarchar(max))+';' like '%;'+T2.item+';%'
SQL Fiddle
The failsafe solution is to split the content of items column into table-like form and then join it to table2.
Say we have these tables:
create table #t1 (id int, items varchar(100));
go
insert #t1 values
( 1, 'aaa;ery;sha;cbre;dezrzyg;'),
( 2, 'aaa;ery;sha;cbre;dezrzyg;'),
( 3, 'dg;e3ry;sd6ha;cb8re;48dz;'),
( 4, 'e5zeza;48;dz;46az;12BREd;'),
(10, 'aaa');
go
create table #t2 (id int, item varchar(100));
go
insert #t2 values
(1, 'aaa'),
(2, 'AAA'),
(3, 'aaa52'),
(4, '2aaa2')
go
We'll use the following approach to split the items:
select substring(items, n, charindex(';', items + ';', n) - n)
from numbers, #t1
where substring(';' + items, n, 1) = ';'
and n < len(items) + 1
This requires a numbers table, see here how to create it.
Here's the whole query:
select distinct #t1.id, #t1.items, case when #t2.id is null then 'doesn''t match' else 'match' end
from #t1
cross apply (
select substring(items, n, charindex(';', items + ';', n) - n)
from numbers
where substring(';' + items, n, 1) = ';'
and n < len(items) + 1
) x (col)
left join #t2 on x.col = #t2.item
--where #t2.id is not null

Is it possible that LEFT JOIN fails while subquery with NOT IN clause suceeds?

A while I have posted an answer to this question PostgreSQL multiple criteria statement.
Task was quite simple - select values from one table if there is no corresponding value in another table. Assuming we have tables like below:
CREATE TABLE first (foo numeric);
CREATE TABLE second (foo numeric);
we would like to get all the values from first.foo which doesn’t occur in the second.foo. I've proposed two solutions:
using LEFT JOIN
SELECT first.foo
FROM first
LEFT JOIN second
ON first.foo = second.foo
WHERE second.foo IS NULL;
combining subquery and IN operator:
SELECT first.foo
FROM first
WHERE first.foo NOT IN (
SELECT second.foo FROM second
);
For some reason the first wouldn't work (returned 0 rows) in the context of the OP and it has been bugging me since then. I've tried to reproduce that issue using different versions of PostgreSQL but no luck so far.
Is there any particular reason why the first solution would fail and the second worked as expected? Am I missing something obvious?
Here is sqlfiddle but it seems to work on any available platform.
Edit
Like #bma and #MostyMostacho pointed out in the comments it should be rather second one that returned no results (sqlfiddle).
As per your sql fiddle, your NOT IN query fails to return results because of the NULL in the second table.
The problem is that NULL means "UNKNOWN" and therefore we cannot say that the following expression is true: 10 not in (5, null).
The reason is what happens when 10 = NULL is compared. We get a NULL back, not a true. This means that a NULL in the NOT IN clause means that no rows will ever pass.
To get the second one to perform the way you expect you have a relatively convoluted query:
SELECT first.foo
FROM first
WHERE (first.foo IN (
SELECT second.foo FROM second
) IS NOT TRUE);
This will properly handle the NULL comparisons, but the join syntax is probably cleaner.
select values from one table if there is no corresponding value in another table. You just answered your own question:
SELECT o.value
FROM table_one o
WHERE NOT EXISTS (
SELECT *
FROM table_two t
WHERE t.value = o.value
);
A short demonstration:
CREATE TABLE first (foo numeric);
CREATE TABLE second (foo numeric);
INSERT INTO first VALUES (1);
INSERT INTO first VALUES (2);
INSERT INTO first VALUES (3);
INSERT INTO first VALUES (4);
INSERT INTO first VALUES (5);
INSERT INTO first VALUES (NULL); -- added this for completeness
INSERT INTO second VALUES (1);
INSERT INTO second VALUES (3);
INSERT INTO second VALUES (NULL);
SELECT f.foo AS ffoo, s.foo AS sfoo
-- these expressions all yield boolean values
, (f.foo = s.foo) AS is_equal
, (f.foo IN (SELECT foo FROM second)) AS is_in
, (f.foo NOT IN (SELECT foo FROM second)) AS is_not_in
, (EXISTS (SELECT * FROM second x WHERE x.foo = f.foo)) AS does_exist
, (NOT EXISTS (SELECT * FROM second x WHERE x.foo = f.foo)) AS does_not_exist
, (EXISTS (SELECT * FROM first x LEFT JOIN second y ON x.foo = y.foo
WHERE x.foo = f.foo AND y.foo IS NULL))
AS left_join_is_null
FROM first f
FULL JOIN second s ON (f.foo = s.foo AND (f.foo IS NOT NULL OR s.foo IS NOT NULL) )
;
Result:
CREATE TABLE
CREATE TABLE
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
ffoo | sfoo | is_equal | is_in | is_not_in | does_exist | does_not_exist | left_join_is_null
------+------+----------+-------+-----------+------------+----------------+-------------------
1 | 1 | t | t | f | t | f | f
2 | | | | | f | t | t
3 | 3 | t | t | f | t | f | f
4 | | | | | f | t | t
5 | | | | | f | t | t
| | | | | f | t | f
| | | | | f | t | f
(7 rows)
As you can see, the boolean can be NULL for the IN() and equals cases.
It cannot be NULL for the EXISTS() case. To be or not to be.
The LEFT JOIN ... WHERE s.foo IS NULL is (almost) equivalent to the NOT EXISTS case, except that it actually includes second.* into the query results (which is not needed, in most cases)

How do I get LIKE and COUNT to return the number of rows less than a value not in the row?

For example:
SELECT COUNT(ID) FROM My_Table
WHERE ID <
(SELECT ID FROM My_Table
WHERE ID LIKE '%4'
ORDER BY ID LIMIT 1)
My_Table:
X ID Y
------------------------
| | A1 | |
------------------------
| | B2 | |
------------------------
| | C3 | |
------------------------ -----Page 1
| | D3 | |
------------------------
| | E3 | |
------------------------
| | F5 | |
------------------------ -----Page 2
| | G5 | |
------------------------
| | F6 | |
------------------------
| | G7 | | -----Page 3
There is no data ending in 4 but there still are 5 rows that end in something less than "%4".
However, in this case were there is no match, so SQLite only returns 0
I get it is not there but how do I change this behavior to still return number of rows before it, as if it was there?
Any suggestions?
Thank You.
SELECT COUNT(ID) FROM My_Table
WHERE ID < (SELECT ID FROM My_Table
WHERE SUBSTRING(ID, 2) >= 4
ORDER BY ID LIMIT 1)
Assuming there is always one letter before the number part of the id field, you may want to try the following:
SELECT COUNT(*) FROM my_table WHERE CAST(substr(id, 2) as int) <= 4;
Test case:
CREATE TABLE my_table (id char(2));
INSERT INTO my_table VALUES ('A1');
INSERT INTO my_table VALUES ('B2');
INSERT INTO my_table VALUES ('C3');
INSERT INTO my_table VALUES ('D3');
INSERT INTO my_table VALUES ('E3');
INSERT INTO my_table VALUES ('F5');
INSERT INTO my_table VALUES ('G5');
INSERT INTO my_table VALUES ('F6');
INSERT INTO my_table VALUES ('G7');
Result:
5
UPDATE: Further to the comment below, you may want to consider using the ltrim() function:
The ltrim(X,Y) function returns a string formed by removing any and all characters that appear in Y from the left side of X.
Example:
SELECT COUNT(*)
FROM my_table
WHERE CAST(ltrim(id, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') as int) <= 4;
Test case (adding to the above):
INSERT INTO my_table VALUES ('ABC1');
INSERT INTO my_table VALUES ('ZWY2');
New Result:
7
In MySQL that would be:
SELECT COUNT(ID)
FROM My_Table
WHERE ID <
(
SELECT id
FROM (
SELECT ID
FROM My_Table
WHERE ID LIKE '%4'
ORDER BY
ID
LIMIT 1
) q
UNION ALL
SELECT MAX(id)
FROM mytable
LIMIT 1
)