I need a query to get output as mention below - sql

I have the data in the table as below.
cntrct_number status_cd registration_date
123 A 23-03-19
123 A 06-06-19
123 S 10-06-21
123 S 11-06-21
123 S 12-06-21
123 A 13-06-21
123 S 14-06-21
123 S 15-06-21
Now I want the two minimum dates of status_cd = 'S'
like the query should give the output as below.
123 S 11-06-21
123 S 14-06-21
The output is that when the status is changed then it should take the first row immediate after the change of status.

You can use where to filter the result by status, then order it by date, and the last step limit the output by 2 rows:
select * from table where status_cd = 'S' order by registration_date limit 2;

What we're looking for is a qualification in a window function.
Qualification 1: the status code is S.
Qualification 2: the previous status code is not S.
create or replace transient table T1(cntrct_number int, status_cd string, registration_date date);
insert into T1 (cntrct_number, status_cd, registration_date) values
(123, 'A', to_date('23-03-19', 'DD-MM-YY')),
(123, 'A', to_date('06-06-19', 'DD-MM-YY')),
(123, 'S', to_date('10-06-21', 'DD-MM-YY')),
(123, 'S', to_date('11-06-21', 'DD-MM-YY')),
(123, 'S', to_date('12-06-21', 'DD-MM-YY')),
(123, 'A', to_date('13-06-21', 'DD-MM-YY')),
(123, 'S', to_date('14-06-21', 'DD-MM-YY')),
(123, 'S', to_date('15-06-21', 'DD-MM-YY'));
select cntrct_number
,status_cd
,registration_date
from T1
qualify STATUS_CD = 'S'
and lag(STATUS_CD) over (partition by cntrct_number order by registration_date) <> 'S'
;

Related

Grouped IF statement in SQL

My data take this basic shape: (http://sqlfiddle.com/#!9/d4ae98/1)
CREATE TABLE Table1
(`ID` int, `Type` varchar(1));
INSERT INTO Table1
(`ID`, `Type`)
VALUES
(123, 'A'),
(123, 'B'),
(123, 'C'),
(456, 'A'),
(789, 'A'),
(789, 'B')
;
What I want is, a third column which is true/false for every row, based on whether that row's ID value has type='B' anywhere in the data. So the desired output would be:
ID Type V3
123 A t
123 B t
123 C t
456 A f
789 A t
789 B t
What is the best way to do this? (And, yes, I am aware that a scripting language like R or Python could easily do what I want here, but I want to use this output as a WITH clause in a larger SQL query.)
You can do this with a Case in the Select:
select *, CASE
WHEN id in (select id from table1 where type like '%B%') then 't'
ELSE 'f'
END V3
from table1;
Fiddle link: http://sqlfiddle.com/#!9/e47bc37/1
May a solution like this one can help you:
with Table2 as (
select * from table1 where type ='B'
)
select t1.*, case t2.type when 'B' then 't' else 'f' end v3 from table1 t1 left
outer join table2 t2 on t1.id = t2.id ;

How to select just the rows in a table that fit a criteria , avoiding duplicates in SQL

So I have a df like follows:
USER Value object
0001 V V
0002 A NULL
0002 C C
0003 A NULL
0004 A NULL
0004 A NULL
0003 V V
So I basically want USER to be the unique id for each row of this DF. If there is an A in the Value column, I only want it if that's the only option for the ID. So there are two 002's, I only want to see the instance where it is not A , so C.
Because 0004 doesn't have a non-A Value, I'll take the A.
Final result:
USER Value
0001 V
0002 C
0003 V
0004 A
I think you are looking for the following:
select user,
'A' as value
from tbl
group by user
having sum(case when value = 'A' then 1 else 0 end) > 0
and sum(case when value <> 'A' then 1 else 0 end) = 0
union all
select user,
value
from tbl
where value <> 'A'
order by user;
See Fiddle:
http://www.sqlfiddle.com/#!9/b28f4c/2/0
The desired result is achieved with your example data. However, your example data does not contain any users having more than one non-A value row. The above query will keep all of them. If you only want to keep one or some, explain how to pick which you want.
This will return the one Value per tuple, returning A at last resort (if A is the smallest of the potential values):
select USER, max(Value) as value from Table
group by User
or, this might return multiple users if they have several tuples with different object (when not null)
select distinct user, coalesce(object, value)
from table ;
Here's a solution if you don't like typing :-)
select
distinct USR
,VAL
from
TBL
qualify
max(ascii(VAL)) over (partition by USR ) = ascii(VAL)
Copy|Paste|Run in snowflake:
CREATE or replace TABLE tbl( USR varchar(4), VAL varchar(1), OBJ varchar(4));
INSERT INTO tbl (USR,VAL,OBJ)
VALUES
('0001', 'V', 'V'),
('0002', 'A', NULL),
('0002', 'C', 'C'),
('0003', 'A', NULL),
('0004', 'A', NULL),
('0004', 'A', NULL),
('0003', 'V', 'V');
select
distinct USR
,VAL
from
TBL
qualify
max(ascii(VAL)) over (partition by USR ) = ascii(VAL);
You can try the following if you are using SQL-Server
select distinct USER
,Value
from
(
select *,rank() over (partition by USER order by Value desc) as ranking
from your_table_name
) as t
where ranking =1

Find the users having more than two elements and one of those elements must be A

I want to extract the users having more than two elements and one of those elements must be A.
This my table:
CREATE TABLE #myTable(
ID_element nvarchar(30),
Element nvarchar(10),
ID_client nvarchar(20)
)
This is the data of my table:
INSERT INTO #myTable VALUES
(13 ,'A', 1),(14 ,'B', 1),(15 ,NULL, 1),(16 ,NULL, 1),
(17 ,NULL, 1),(18 ,NULL, 1),(19 ,NULL, 1),(7, 'A', 2),
(8, 'B', 2),(9, 'C', 2),(10 ,'D', 2),(11 ,'F', 2),
(12 ,'G', 2),(1, 'A', 3),(2, 'B', 3),(3, 'C', 3),
(4, 'D', 3),(5, 'F', 3),(6, 'G', 3),(20 ,'Z', 4),
(22 ,'R', 4),(23 ,'D', 4),(24 ,'F', 5),(25 ,'G', 5),
(21 ,'x', 5)
And this is my query:
Select Distinct ID_client
from #myTable
Group by ID_client
Having Count(Element) > 2
Add to your query CROSS APPLY with id_clients that have element A
SELECT m.ID_client
FROM #myTable m
CROSS APPLY (
SELECT ID_client
FROM #myTable
WHERE ID_client = m.ID_client
AND Element = 'A'
) s
GROUP BY m.ID_client
HAVING COUNT(DISTINCT m.Element) > 2
Output:
ID_client
2
3
I think this is what you are looking for:
SELECT * FROM
(SELECT *, RANK() OVER (PARTITION BY element ORDER by id_client) AS grouped FROM #myTable) t
wHERE grouped > 1
AND Element = 'A'
ORDER by t.element
which brings back
ID_element Element ID_client grouped
7 A 2 2
1 A 3 3
You can select the ID_client values which have an 'A' as an Element and join your table with the result of that:
SELECT m.ID_Client
FROM #myTable AS m
JOIN (
SELECT a.ID_Client FROM #myTable AS a
WHERE a.Element = 'A') AS filteredClients
ON m.ID_client = filteredClients.ID_client
GROUP BY m.ID_client
HAVING COUNT(m.Element) > 2
Outputs:
ID_Client
2
3
However, this is not necessarily the best way to do it: When should I use Cross Apply over Inner Join?

SQL query to get rows from two tables

I have two tables with ID and value columns.
I want to union these two tables but if ID exists in second table, I want to discard all same IDs in first table and retrieve only these IDs from second table. How can I create the query for this?
First table:
ID Value
100 1
100 2
101 3
102 4
Second table:
ID Value
100 5
100 6
100 7
102 5
The result I want to achieve:
ID Value
100 5
100 6
100 7
101 3
102 5
I tried to do as suggested but it still returns only values from table 1:
String selectQuery = "SELECT * FROM " + TABLE1_NAME
+" UNION ALL"
+" SELECT * FROM " + TABLE2_NAME
+" WHERE "+id+" NOT IN (SELECT "+id+" FROM "+TABLE2_NAME+ ")";
try
select id,value from table1
union ALL
select id , value from table2
where id not in (select id from table1)
edit as suggested by Ormoz :
To use table2's result if both tables have the ids :
select id,value from table2
union ALL
select id , value from table1
where id not in (select id from table2)
My tests:
create table table1 (id int not null, value int not null);
insert into table1 values
(100, 1),
(100, 2),
(101, 3),
(102, 4);
create table table2 (id int not null, value int not null);
insert into table2 values
(100, 5),
(100, 6),
(100, 7),
(102, 5);
select id,value , 't2' as t from table2
union ALL
select id , value, 't1' from table1
where id not in (select id from table2);
this is the output:
# id, value, t
'100', '5', 't2'
'100', '6', 't2'
'100', '7', 't2'
'102', '5', 't2'
'101', '3', 't1'
Maybe this could do the trick?
(
SELECT id, value FROM table2
UNION ALL
SELECT id, value FROM table1
) GROUP BY id
My problem is solved by removing the where clause to first select:
String selectQuery = "SELECT * FROM " + TABLE1_NAME
+" WHERE "+id+" NOT IN (SELECT "+id+" FROM "+TABLE2_NAME)
+" UNION ALL"
+" SELECT * FROM " + TABLE2_NAME;
Special thanks to #Tim3880 and #Ormoz =)

SQL query for 3 rows with the min date, particular file extensions

In the table
CREATE TABLE "FILES"
(
"ID_FILE" NUMBER(38,0) NOT NULL ENABLE,
"REPORT_DATE" DATE,
"NAME" VARCHAR2(128 BYTE),
"PROCESSED" VARCHAR2(1 BYTE)
)
There are records with different dates, names that end on 'p1', 'l1', 'm1' and whether they are processed or not - 'N' and 'Y' respectively.
I want to choose from the earliest date, unprocessed files, package of these 3 names that end of 'p1', 'l1', 'm1'.
Oracle query:
SELECT REPORT_DATE, DISTINCT NAME
FROM
( SELECT *
FROM FILES)
WHERE
( NAME LIKE '%p1' OR
NAME LIKE '%l1' OR
NAME LIKE '%m1') AND
ROWNUM = 3 AND
PROCESSED = 'N'
GROUP BY REPORT_DATE
HAVING MIN(REPORT_DATE);
The error is :
Error at Line: 12 Column: 23 (having clause)
Edit:
the records are like :
(1, '11/05/2011', 'some_name_p1', 'N')
(2, '11/05/2011', 'some_name_l1', 'N')
(3, '11/05/2011', 'some_name_m1', 'N')
(4, '11/05/2011', 'some_name_k1', 'Y')
(5, '11/05/2012', 'some_name_p1', 'n')
(6, '11/05/2011', 'some_name_m1', 'N')
The query should select only the first 3 rows(1,2,3), nothing more. These 3 rows, must contain all three 'p1', 'l1', 'm1' extensions in the names.
A HAVING clause is typically used for comparisons with aggregate functions. What you are probably looking for is to match the REPORT_DATE with the minimum value, which you can use a sub query for:
SELECT
REPORT_DATE, DISTINCT NAME
FROM
(SELECT * FROM FILES)
WHERE
(NAME LIKE '%p1' OR
NAME LIKE '%l1' OR
NAME LIKE '%m1')
AND ROWNUM = 3
AND PROCESSED = 'N'
AND REPORT_DATE = (SELECT MIN(REPORT_DATE) FROM FILES WHERE NAME LIKE '%p1' OR NAME LIKE '%l1' OR NAME LIKE '%m1')
GROUP BY
REPORT_DATE;