UNION to one Line with Only 'Yes' Values - sql

I have a table that in an ideal world should only return 1 row per 'policy' for items that were sold as part of an up-sell.
I wish to roll this up into one line per 'PolRef#' and basically the 'Yes' should supercede 'No' should it exist in the column.
B# PolRef# Uk Eu Date Ep500 Ep700 Ep3000 Keycare Wind Ep350 Ep250 Legal Totaladdon Finance_yn
2 ROGX17PC01 Yes No 2017-07-31 00:00:00.000 No No No No No NULL NULL NULL 62.00 Yes
2 ROGX17PC01 No No 2017-07-31 00:00:00.000 No No No No No NULL NULL Yes 32.00 Yes
This is an example, I know I could do a GROUP BY to GROUP the PolRef# and then SUM the TotalAddon, how however can I have it so that if 'Yes' exists in A column for that 'PolRef#' that is shows it.
Essentially above's result should look like
B# PolRef# Agent Uk Eu Date Ep500 Ep700 Ep3000 Keycare Wind Ep350 Ep250 Legal Totaladdon Finance_yn
2 ROGX17PC01 NULL Yes No 2017-07-31 00:00:00.000 No No No No No NULL NULL Yes 94.00 Yes

This is a prioritization query. One method uses row_number():
select t.*
from (select t.*,
row_number() over (partition by PolRef#
order by uk desc -- 'Yes' comes before 'No'
) as seqnum
from t
) t
where seqnum = 1;

Related

How to write SQLite code to show a specified code along with its associated codes from group_concat function?

I am trying to create a table where the code N09 is included, where a student was assigned a set of codes that contains N09, and "Status Complete" was yes. I wanted to use group_concat to see if each set contains N09. I saw a similar question to this but unfortunately, it did not satisfy my goal for Table 2 as it led to a problem. This problem I am experiencing is that it keeps showing 1 instead of 2, 3 for count. It also keeps showing N09, instead of N09 and its other codes from the set from the group_concat function. Is there a code to achieve my goal for Table 2 in SQLite? If my question is not clear, feel free to comment as I am new here.
Goal for Table 2:
Student ID
Status Complete
Status Date
Status Time
Code
Count
Group_Concat(Code)
1
yes
03/03/2021
00:00:00
N09
1
N09
2
yes
03/04/2021
10:03:10
N09
2
N09, M33
3
yes
03/04/2021
01:00:10
N09
3
N09, Y03, B55
Problem:
Student ID
Status Complete
Status Date
Status Time
Code
Count
Group_Concat(Code)
1
yes
03/03/2021
00:00:00
N09
1
N09
2
yes
03/04/2021
10:03:10
N09
1
N09
3
yes
03/04/2021
01:00:10
N09
1
N09
Sample Data:
Student ID
Status Complete
Status Date
Status Time
Code
1
yes
03/03/2021
00:00:00
N09
2
yes
03/04/2021
10:03:10
N09
2
yes
03/04/2021
10:03:10
M33
3
yes
03/04/2021
01:00:10
N09
3
yes
03/04/2021
01:00:10
Y03
3
yes
03/04/2021
01:00:10
B55
Code:
CREATE TABLE table2 AS
select Student_ID
,Status_Complete
,Status_Date
,Status_TIME
,Code
,count(Code) /*over (partition by Student_ID,Code)*/ as 'Count'
,GROUP_CONCAT(Code)
from table1
where Code in ('N09') AND Status_Complete = 'yes'
group by Student_ID, Status_Date, Status_TIME, 'Count'
HAVING 'Count'> 0
ORDER BY Student_ID;
You should group by Student_ID only since you want only 1 row for each student.
The columns Status_Date and Status_TIME of the results that you want seem to be the min values of each student (I assume that the dates have the proper format of YYYY-mm-dd which is the only valid date format for SQLite).
Also, the condition Code = 'N09' should be checked in the HAVING clause:
CREATE TABLE table2 AS
SELECT Student_ID, Status_Complete,
MIN(Status_Date) Status_Date,
TIME(MIN(Status_Date || ' ' || Status_TIME)) Status_TIME,
COUNT(*) count,
GROUP_CONCAT(Code) Codes
FROM table1
WHERE Status_Complete = 'yes'
GROUP BY Student_ID
HAVING SUM(Code = 'N09') > 0
ORDER BY Student_ID;
See the demo.
Never use single quotes for column names.
'Count' is a string literal when used in code. It never refers to a column alias.
the WHERE cluase you have excludes all columns that are not N09 and have the status completed, so switch zu a EXISTS clause
As Lennart points out, here the having is redundant, as all rows now will have at least the count of 1
CREATE TABLE table2 AS
select Student_ID
,Status_Complete
,Status_Date
,Status_TIME
,Code
,count(Code) /*over (partition by Student_ID,Code)*/ as 'Count'
,GROUP_CONCAT(Code)
from table1 t1
where EXISTS( (SELECT 1 FROM table1 WHERR Code in ('N09') AND Status_Complete = 'yes' AND Student_ID = t1.Student_ID)
group by Student_ID, Status_Date, Status_TIME
ORDER BY Student_ID;

Counting and adding distinct values that occur in certain dates using PostgreSQL

Using some SQL in the tables of some database, I get a result like this:
id name date status
1 John 2018-05-03 PRESENT
2 Mary 2018-05-03 NOT PRESENT
3 Jane 2018-05-03 NOT PRESENT
2 Mary 2018-05-04 PRESENT
1 John 2018-05-04 PRESENT
1 John 2018-05-05 PRESENT
2 Mary 2018-05-05 NOT PRESENT
3 Jane 2018-05-04 PRESENT
3 Jane 2018-05-05 NOT PRESENT
1 John 2018-05-06 PRESENT
I wanna use further SQL to get in a result like this one:
id name date present not present
1 John 2018-05 4 0
2 Mary 2018-05 1 2
3 Jane 2018-05 2 1
In other words, I wanna extract how many classes a student attended in a given month, based on the status he/she received everyday. How can I achieve that?
Use conditional aggregation :
select id, name, to_char(date,'YYYY-MM') as "Date",
sum(case when status = 'PRESENT' then 1 else 0 end ) as present,
sum(case when status = 'NOT PRESENT' then 1 else 0 end ) as not_present
from tab
group by id, name, "Date"
order by id
Demo
keeping else 0 is important to get 0 for null returning cases
column alias in the select list might be used in the group by list
for Postgres
due to the desired output, truncating date value to month by
to_char(date,'YYYY-MM') is needed
select id, name, to_char(date,'YYYY-MM') as date,
sum((case when status = 'PRESENT' then 1 end )) present,
sum((case when status = 'NOT PRESENT' then 1 end )) not_present
from your_result_table
group by id, name, to_char(date,'YYYY-MM')
Use conditional aggregation (using filter) and date_trunc():
select id, name, date_trunc('month', date),
count(*) filter (where status = 'PRESENT') as num_present,
count(*) filter (where status = 'NOT PRESENT') as num_notpresent
from t
group by id, name, date_trunc('month', date)
order by id, name, date_trunc('month', date)

Expanding/changing my query to find more entries using (potentially) IFELSE

My question will use this dataset as an example. I have a query setup (I have changed variables to more generic variables for the sake of posting this on the internet so the query may not make perfect sense) that picks the most recent date for a given account. So the query returns values with a reason_type of 1 with the most recent date. This query has effective_date set to is not null.
account date effective_date value reason_type
123456 4/20/2017 5/1/2017 5 1
123456 1/20/2017 2/1/2017 10 1
987654 2/5/2018 3/1/2018 15 1
987654 12/31/2017 2/1/2018 20 1
456789 4/27/2018 5/1/2018 50 1
456789 1/24/2018 2/1/2018 60 1
456123 4/25/2017 null 15 2
789123 5/1/2017 null 16 2
666888 2/1/2018 null 31 2
333222 1/1/2018 null 20 2
What I am looking to do now is to basically use that logic to only apply to reason_type
if there is an entry for it, otherwise have it default to reason_type
I think I should be using an IFELSE, but I'm admittedly not knowledgeable about how I would go about that.
Here is the code that I currently have to return the reason_type 1s most recent entry.
I hope my question is clear.
SELECT account, date, effective_date, value, reason_type
from
(
SELECT account, date, effective_date, value, reason_type
ROW_NUMBER() over (partition by account order by date desc) rn
from mytable
WHERE value is not null
AND effective_date is not null
)
WHERE rn =1
I think you might want something like this (do you really have a column named date by the way? That seems like a bad idea):
SELECT account, date, effective_date, value, reason_type
FROM (
SELECT account, date, effective_date, value, reason_type
, ROW_NUMBER() OVER ( PARTITION BY account ORDER BY date DESC ) AS rn
FROM mytable
WHERE value IS NOT NULL
) WHERE rn = 1
-- effective_date IS NULL or is on or before today's date
AND ( effective_date IS NULL OR effective_date < TRUNC(SYSDATE+1) );
Hope this helps.

SQL Query - Design struggle

I am fairly new to SQL Server (2012) but I was assigned the project where I have to use it.
The database consists of one table (counted in millions of rows) which looks mainly like this:
Number (float) Date (datetime) Status (nvarchar(255))
999 2016-01-01 14:00:00.000 Error
999 2016-01-02 14:00:00.000 Error
999 2016-01-03 14:00:00.000 Ok
999 2016-01-04 14:00:00.000 Error
888 2016-01-01 14:00:00.000 Error
888 2016-01-02 14:00:00.000 Ok
888 2016-01-03 14:00:00.000 Error
888 2016-01-04 14:00:00.000 Error
777 2016-01-01 14:00:00.000 Error
777 2016-01-02 14:00:00.000 Error
I have to create a query which will show me only the phone numbers (one number per row so probably Group by number?) that meet the conditions:
Number reappears at least 3 times
Last two times (that has to be based on date; originally records are not sorted by date) has to be an Error
For example, in the table above the phone number that meets the criteria is only 888, beacuse for 999 2nd newest status is Ok and number 777 reoccurs only 2 times.
I will appreciate any kind of help!
Thanks in advance!
You can use row_number() and conditional aggregation:
select number
from (select t.*,
row_number() over (partition by number order by date desc) as seqnum
from t
) t
group by number
having count(*) >= 3 and
max(case when seqnum = 1 then status end) = 'Error' and
max(case when seqnum = 2 then status end) = 'Error';
Note: float is a really, really bad type to use for the "number" column. In particular, two numbers can look the same but differ in low-order bits. They will produce different rows in the group by.
You should probably use varchar() for telephone numbers. That gives you the most flexibility. If you need to store the number as a number, then decimal/numeric is a much, much better choice than float.
select *, ROW_NUMBER() OVER(partition by Number, order by date desc) as times
FROM
(
select Number, Date
From table
where Number in
(
select Number
from table
group by Number
having count (*) >3
) as ABC
WHERE ABC.times in (1,2) and ABC.Status = 'Error'
with CTE as
(
select t1.*, row_number() over(partition by t1.Number order by t1.date desc) as r_ord
from MyTable t1
)
select C1.*
from CTE C1
inner join
(
select Number
from CTE
group by Number
having max(r_ord) >=3
) C2
on C1.Number = C2.Number
where C1.r_ord in (1,2)
and C1.Status = 'Error'

Select distinct records based on max(date) or NULL date

I am trying to get a list of employees based on their employee status or their most recent termination date. If the employee is active, the termination date will be NULL. There are also employees that have worked in multiple companies within our organization, I only want the record from the most recent company, whether active or terminated. An employee may also have different Employee numbers in the different companies, so the selection will have to be based on the SSN (Fica) number.
Here is an original data set:
company employee Fica First_name emp_status Term_date
5 7026 Jason T1 2013-09-16 00:00:00.000
500 7026 Jason T1 2010-11-30 00:00:00.000
7 7026 Jason T1 2009-07-31 00:00:00.000
2 90908 Jason A1 NULL
505 293866 William T1 2008-05-23 00:00:00.000
7 7243 Ashley T1 2010-07-11 00:00:00.000
2 90478 Michael T1 2013-01-11 00:00:00.000
500 90478 Michael T1 2011-09-26 00:00:00.000
500 311002 Andreas A1 NULL
3 365463 Matthew A1 NULL
500 248766 Chris T1 2007-04-23 00:00:00.000
500 90692 Kaitlyn T1 2012-03-13 00:00:00.000
2 90692 Kaitlyn A5 NULL
500 90236 Jeff T1 2011-09-26 00:00:00.000
2 90236 Jeff A1 NULL
2 90433 Nathan T1 2012-03-26 00:00:00.000
500 90433 Nathan T1 2011-09-26 00:00:00.000
Here are the results I am trying to get:
company employee Fica First_name emp_status Term_date
2 90908 Jason A1 NULL
505 293866 William T1 2008-05-23 00:00:00.000
7 7243 Ashley T1 2010-07-11 00:00:00.000
2 90478 Michael T1 2013-01-11 00:00:00.000
500 311002 Andreas A1 NULL
3 365463 Matthew A1 NULL
500 248766 Chris T1 2007-04-23 00:00:00.000
2 90692 Kaitlyn A5 NULL
2 90236 Jeff A1 NULL
2 90433 Nathan T1 2012-03-26 00:00:00.000
Thanks for any help you are able to give. I need to run this on a SQL2005 server which will be connecting to an Oracle server via ODBC.
If the dates were all populated, you could do this with a "standard" not exists query. The NULLs introduce a problem, but that problem can be solved using coalesce():
select t.*
from table t
where not exists (select 1
from table t2
where t2.employee = t.employee and
coalesce(t2.term_date, '9999-01-01') > coalesce(t.term_date, '9999-01-01)
);
NOTE: If you need for this to work on Oracle, then you need a different format for the date constant.
EDIT:
Another way to solve this uses row_number():
select t.*
from (select t.*,
row_number() over (partition by employee
order by (case when term_date is null then 0 else 1 end),
term_date desc
) as seqnum
from table t
) t
where seqnum = 1;
The rule for choosing the "last" row are embedded in the order by clause. Put the NULL value first, followed by the term_date in descending order.