sql finding cid with most expired cards [closed] - sql

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 months ago.
Improve this question
I have a table Cards(card_id,status,cid)
With the columns:
cid - customer id
status - exp/vld
card_id - card id's
How to find the cid with the most expired cards?

From Oracle 12, you can use:
SELECT cid,
COUNT(*) AS num_exp
FROM cards
WHERE status = 'exp'
GROUP BY cid
ORDER BY num_exp DESC
FETCH FIRST ROW WITH TIES;

You can get count of expired cards for individual customers and then choose customer with MAX count. The below query should give results.
WITH t AS(
SELECT cid, count(1) customer_exp_cards_count
FROM Cards where status = 'exp'
group by cid)
SELECT cid FROM t t1
WHERE t1.customer_exp_cards_count IN (SELECT MAX(t2.customer_exp_cards_count)
FROM t t2)
Sample data and its result:
cardid status cid
3 exp 5
1 exp 1
2 exp 1
3 vld 1
5 vld 1
1 exp 2
2 exp 2
3 exp 2
4 vld 2
5 vld 2
6 exp 3
7 vld 4
4 vld 5
Result:
2

Suppose you have these two tables (just a sample data)
CUSTOMERS
CUST_ID
CUST_NAME
CUST_STATUS
101
John
ACTIVE
102
Annie
ACTIVE
103
Jane
ACTIVE
104
Bob
INACTIVE
CARDS
CARD_ID
CARD_STATUS
CUST_ID
1001001
VALID
101
1001002
VALID
101
1001003
EXPIRED
101
1001004
EXPIRED
101
1001005
VALID
101
1002010
VALID
102
1002020
EXPIRED
102
1002030
EXPIRED
102
1002040
EXPIRED
102
1003100
VALID
103
1003200
VALID
103
If you want just a CUST_ID with the number of most expired cards you can do it without table CUSTOMERS:
Select CUST_ID, EXPIRED_CARDS
From (Select CUST_ID, Count(CARD_ID) "EXPIRED_CARDS" From cards Where CARD_STATUS = 'EXPIRED' Group By CUST_ID)
Where EXPIRED_CARDS = (Select Max(EXPIRED_CARDS) From (Select Count(CARD_ID) "EXPIRED_CARDS" From cards Where CARD_STATUS = 'EXPIRED' Group By CUST_ID) )
--
-- R e s u l t
-- CUST_ID EXPIRED_CARDS
-- ---------- -------------
-- 102 3
Maybe you could consider creating a CTE with the data from both tables which will give you dataset that you could use later for different questions not just for this one. Something like this:
WITH
customers_cards AS
(
Select
cst.CUST_ID,
cst.CUST_NAME,
cst.CUST_STATUS,
crd.CARD_ID,
crd.CARD_STATUS,
Sum(CASE WHEN crd.CUST_ID Is Null Then 0 Else 1 End) OVER(Partition By crd.CUST_ID) "TOTAL_NUM_OF_CARDS",
Sum(CASE WHEN crd.CARD_ID Is Null Then Null WHEN crd.CARD_STATUS = 'VALID' And crd.CARD_ID Is Not Null Then 1 Else 0 End) OVER(Partition By crd.CUST_ID) "VALID_CARDS",
Sum(CASE WHEN crd.CARD_ID Is Null Then Null WHEN crd.CARD_STATUS = 'EXPIRED' And crd.CARD_ID Is Not Null Then 1 Else 0 End) OVER(Partition By crd.CUST_ID) "EXPIRED_CARDS"
From
customers cst
Left Join
cards crd on(crd.CUST_ID = cst.CUST_ID)
)
/* R e s u l t :
CUST_ID CUST_NAME CUST_STATUS CARD_ID CARD_STATUS TOTAL_NUM_OF_CARDS VALID_CARDS EXPIRED_CARDS
---------- --------- ----------- ------- ----------- ------------------ ----------- -------------
101 John ACTIVE 1001001 VALID 5 3 2
101 John ACTIVE 1001002 VALID 5 3 2
101 John ACTIVE 1001003 EXPIRED 5 3 2
101 John ACTIVE 1001004 EXPIRED 5 3 2
101 John ACTIVE 1001005 VALID 5 3 2
102 Annie ACTIVE 1002010 VALID 4 1 3
102 Annie ACTIVE 1002040 EXPIRED 4 1 3
102 Annie ACTIVE 1002030 EXPIRED 4 1 3
102 Annie ACTIVE 1002020 EXPIRED 4 1 3
103 Jane ACTIVE 1003100 VALID 2 2 0
103 Jane ACTIVE 1003200 VALID 2 2 0
104 Bob INACTIVE 0
*/
This can be used to answer many more potential questions. Here is the list of customers sorted by number of expired cards (descending):
Select Distinct
CUST_ID, CUST_NAME, TOTAL_NUM_OF_CARDS, VALID_CARDS, EXPIRED_CARDS
From
customers_cards
Order By
EXPIRED_CARDS Desc Nulls Last, CUST_ID
--
-- R e s u l t :
-- CUST_ID CUST_NAME TOTAL_NUM_OF_CARDS VALID_CARDS EXPIRED_CARDS
-- ---------- --------- ------------------ ----------- -------------
-- 102 Annie 4 1 3
-- 101 John 5 3 2
-- 103 Jane 2 2 0
-- 104 Bob 0
OR to answer your question:
Select Distinct
CUST_ID, CUST_NAME, TOTAL_NUM_OF_CARDS, VALID_CARDS, EXPIRED_CARDS
From
customers_cards
Where
EXPIRED_CARDS = (Select Max(EXPIRED_CARDS) From customers_cards)
Order By
CUST_ID
--
-- R e s u l t :
-- CUST_ID CUST_NAME TOTAL_NUM_OF_CARDS VALID_CARDS EXPIRED_CARDS
-- ---------- --------- ------------------ ----------- -------------
-- 102 Annie 4 1 3
Regards...

Related

Count values separately until certain amount of duplicates SQL

I need a Statement that selects all patients and the amount of their appointments and when there are 3 or more appointments that are taking place on the same date they should be counted as one appointment
That is what my Statement looks so far
SELECT PATSuchname, Count(DISTINCT AKTDATUM) AS AKTAnz
FROM tblAktivitaeten
LEFT OUTER JOIN tblPatienten ON (tblPatienten.PATID=tblAktivitaeten.PATID)
WHERE (AKTDeleted<>'J' OR AKTDeleted IS Null)
GROUP BY PATSuchname
ORDER BY AKTAnz DESC
The result should look like this
PATSuchname Appointments
----------------------------------------
Joey Patner 13
Billy Jean 15
Example Name 13
As you can see Joey Patner has 13 Appointments, in the real table though he has 15 appointments but three of them have the same Date and because of that they are only counted as 1
So how can i write a Statement that does exactly that?
(I am new to Stack Overflow, sorry if the format I use is wrong and tell me if it is.
In the table it looks like this.
tblPatienten
----------
PATSuchname PATID
------------------------
Joey Patner 1
Billy Jean 2
Example Name 3
tblAktivitaeten
----------
AKTDatum PATID AKTID
-----------------------------------------
08.02.2021 1 1000 ----
08.02.2021 1 1001 ---- So these 3 should counted as 1
08.02.2021 1 1002 ----
09.05.2021 1 1003
09.07.2021 2 1004 -- these 2 shouldn't be counted as 1
09.07.2021 2 1005 --
Two GROUP BY should do it:
SELECT
x.PATID, PATSuchname, SUM(ApptCount)
FROM (
SELECT
PATID, AKTDatum, CASE WHEN COUNT(*) < 3 THEN COUNT(*) ELSE 1 END AS ApptCount
FROM tblAktivitaeten
GROUP BY
PATID, AKTDatum
) AS x
LEFT JOIN tblPatienten ON tblPatienten.PATID = x.PATID
GROUP BY
x.PATID, PATSuchname

Select max date for each register, null if does not exists

I have these tables: Employee (id, name, number), Configuration (id, years, licence_days), Periods (id, start_date, end_date, configuration_id, employee_id, period_type):
Employee table:
id name number
---- ----- -------
1 Bob 355
2 John 467
3 Maria 568
4 Josh 871
configuration table:
id years licence_days
---- ----- ------------
1 1 8
2 3 16
3 5 24
Periods table:
id start_date end_date configuration_id employee_id period_type
---- ---------- ------- ---------------- ----------- -----------
1 2021-05-23 2021-05-31 1 1 vaccation
2 2021-05-24 2021-06-01 1 2 vaccation
3 2021-03-01 2021-03-17 2 2 vaccation
4 2021-05-05 2021-05-21 2 2 vaccation
5 2021-01-01 2021-01-17 2 4 vaccation
I want this result:
Result:
employee_id years licence_days max(end_date)
1 1 8 2021-05-31
1 3 16 null
1 5 24 null
2 1 8 2021-06-01
2 3 16 2021-05-21
2 5 24 null
3 1 8 null
3 3 16 null
3 5 24 null
4 1 8 null
4 3 16 2021-01-17
4 5 24 null
i.e., I want to select all Employees with all configuration, and for each one of that, the max end_date of the "vaccation" type (or null if it does not exists).
How can I do that
Oracle supports cross joins, right? So may be something like that?
SELECT e.employee_id, c.years, c.licence_days, max(p.end_date)
FROM Employee e
CROSS JOIN configuration c
LEFT JOIN Periods p
ON e.employee_id = p.employee_id
AND c.configuration_id = p.configuration_id
GROUP BY e.employee_id, c.years, c.licence_days
ORDER BY e.employee_id, c.years
#umberto-petrov chooses wisely with the ANSI CROSS JOIN syntax for a cartesian join. However, in the very weak probability that your requires output of configurations even where there is no employees, you can go with something like :
EDIT: Filtering the Periods join with 'vaccation' as asked in the comments.
If you have to filter for some employee ids, change ON 1 = 1 by ON Employee.id IN (id1, id2, ...). It still keeps every configurations but only takes employees that match the ids.
SELECT Employee.employee_id,
Configuration.years,
Configuration.licence_days,
MAX(Configuration.end_date) max_end_date
FROM Configuration LEFT JOIN Employee ON 1 = 1
LEFT JOIN Periods ON Periods.configuration_id = Configuration.id
AND Periods.employee_id = Employee.id
AND Periods.period_type = 'vaccation'
GROUP BY Employee.employee_id,
Configuration.years,
Configuration.licence_days
ORDER BY Employee.employee_id,
Configuration.years,
Configuration.licence_days
We start from configuration to take every records from this one at least, then made a LEFT CARTESIAN JOIN with Employee and finally a full LET JOIN on Periods for both. That way , if there is no employees, this will output configuration_id and NULL for years, licence_days and max end_date.

Selecting latest entry ordered by group, adding a user defined column for the latest one

I currently have two SQL tables:
Employees
ID Created RecordID Status
------ ----------------------- ------------ ----------
1 2020-07-29 12:38:54.070 1 1
2 2020-08-03 14:28:59.803 1 1
3 2020-08-04 13:47:49.427 2 0
and
Payments
ID EmployeeID Amount
------ -------------- ------------
1 1 1022
2 1 1090
3 1 2105
4 2 1112
5 2 1450
6 2 2923
7 3 1064
How do I add another (user defined) column called Latest to the following query, that can check if the row corresponds to the Employee RecordID number with the latest Created, and returns a string true or false value for that row? In other words, a row should be Latest: true if its Created has the latest timestamp for all rows corresponding to that RecordID number.
Here's my SQL so far:
SELECT e.ID, e.Created, e.Status, p.EmployeeID, p.Amount
FROM Employees AS e
JOIN Payments AS p
ON e.ID = p.EmployeeID
Here are the expected results:
ID Created Status EmployeeID Amount Latest
------ --------------------------- ---------- -------------- ---------- ----------
1 2020-07-29 12:38:54.070 1 1 1022 false
1 2020-07-29 12:38:54.070 1 1 1090 false
1 2020-07-29 12:38:54.070 1 1 2105 false
2 2020-08-03 14:28:59.803 1 2 1112 true
2 2020-08-03 14:28:59.803 1 2 1450 true
2 2020-08-03 14:28:59.803 1 2 2923 true
3 2020-08-04 13:47:49.427 0 3 1064 true
Thanks!
;WITH x AS
(
SELECT e.ID, e.Created, e.Status, p.EmployeeID, p.Amount,
Latest = CASE DENSE_RANK() OVER (PARTITION BY e.RecordID ORDER BY e.Created DESC)
WHEN 1 THEN 'true' ELSE 'false' END
FROM dbo.Employees AS e
JOIN dbo.Payments AS p
ON e.ID = p.EmployeeID
)
SELECT * FROM x ORDER BY ID;

Removing Duplicates Based date

I have the following Query to select (will use for an update statement) remove duplicates based on the min service date and keeping the most recent svc date.
select st.SubID, st.RecordNo, st.Fname, st.Lname, st.MemberID, st.ServiceDate, IsDeduped, DedupCriteria
from stagingtable st
join (select MemberID
from stagingtable
where SubID = 99999
and waveseqid = 1
group by MemberID
having count(*) > 1) st2
on st.MemberID = st2.MemberID
and st.ServiceDate = (Select min(ServiceDate) from stagingtable s where s.subid = 99999 and s.waveseqid = 1 and st.MemberID = s.MemberID)
where SubID = 99999
and waveseqid = 1
order by RecordNo
This seems to pull in sometime only pull in multiples with the same date for the memberid:
SurveyID RecordNo Fname Lname MemberID Option9 IsDeduped DedupCriteria
99999 1 John Doe 123 10/1/2015 0 NULL x These show on the query
99999 2 John Doe 123 10/1/2015 0 NULL x These show on the query
99999 3 John Doe 123 10/8/2015 0 NULL But expected these as well
99999 4 John Doe 123 10/12/2015 0 NULL But expected these as well
99999 4 John Doe 123 10/14/2015 0 NULL But expected these as well
99999 6 John Doe 123 10/29/2015 0 NULL But expected these as well
99999 7 John Doe 123 12/14/2015 0 NULL But expected these as well
Your "AND" statement restricts the results to only rows with the minimum service date.
and st.ServiceDate = (Select min(ServiceDate) from stagingtable s where s.subid = 99999 and s.waveseqid = 1 and st.MemberID = s.MemberID)
That's why you get two rows and not all of them.

get multiple column from both table by joining a table on the basis of other max table column

1) vendor table
--------------------------------------------
VENDid VENDname
--- -----
1 ABC
2 XYZ
3 WXY
2)purchase table
---------------------------------------------
VENDid Purchasedate
------ ------------
1 12-01-2012
1 10-11-2013
2 22-02-2014
2 11-04-2014
3 10-05-2014
3 11-06-2014
1 14-06-2014
output(list all rows of vendor table and only max(purchasedate) from purchase table)
---------------------------------------------
VENDid VENDname PurchaseDate
------- -------- -------------
1 ABC 14-06-2014
2 XYZ 11-04-2014
3 WXY 11-06-2014
i got some queries like to solve previous problem-
SELECT v.VendID, VendName, Max(PurchaseDate)
FROM vendor v
INNER JOIN purchase p
ON v.VendID = p.VendID
Group By v.VendID, VendName
select VENDid, VENDname,
(select top 1 purchaseDate from purchase p
where p.VENDid=v.VENDid order by purchaseDate desc) as 'Purchase date'
from Vendor v
Que. If i will add some more column in purchase table like -
2)purchase table
------------------------------------------
VENDid Purchasedate amount_paid
------ ------------ ------------
1 12-01-2012 10000
1 10-11-2013 20000
2 22-02-2014 15000
2 11-04-2014 30000
3 10-05-2014 80000
3 11-06-2014 17000
1 14-06-2014 28000
and i want amount_paid along with previous output like-
---------------------------------------------
VENDid VENDname PurchaseDate amount_paid
------- -------- ------------- -------------
1 ABC 14-06-2014 28000
2 XYZ 11-04-2014 30000
3 WXY 11-06-2014 17000
then what will be query..
You appear to be using SQL Server. If so, you can use cross apply:
select v.VENDid, v.VENDname, p.PurchaseDate, p.Amount_Paid
from Vendor v cross apply
(select top 1 p.*
from purchase p
where p.VENDid = v.VENDid
order by p.purchaseDate desc
) p ;