Find the first key by date field using sql and output also have other fields - sql

I want to query the first occurrence of every name according to the earliest date. The output should have the complete row. Please help me to write the query in sql.
Input:
Name | ID | payment_date | Pack
------+-------+-----------------+-------
A | 11 | 31-Jan | P
C | 13 | 31-Jan | Q
B | 2 | 31-Jan | R
C | 3 | 28-Jan | P
D | 23 | 29-Jan | Q
B | 11 | 30-Jan | R
A | 17 | 25-Jan | P
C | 13 | 26-Jan | Q
D | 17 | 2-Feb | R
B | 23 | 3-Feb | P
A | 45 | 4-Feb | Q
B | 3 | 5-Feb | R
Output:
Name | ID | payment_date | Pack
-----+-------+--------------+-----
A | 17 | 25-Jan | P
B | 11 | 30-Jan | R
C | 13 | 26-Jan | Q
D | 23 | 29-Jan | Q

You can use the min function, also assuming payment_date is a date type:
select Name, ID, min(payment_date), Pack from mytable
group by payment_date,Name, ID, Pack
order by Name
The downfall about this method is putting all of the fields in the group by.

If your payment_date is a date data type, you can use not exists() like so:
select *
from t
where not exists (
select 1
from t i
where i.Name = t.Name
and i.payment_date < t.payment_date
)
rextester demo (sql server): http://rextester.com/OKB46268
returns
+------+----+-------------+------+
| Name | Id | PaymentDate | Pack |
+------+----+-------------+------+
| A | 17 | 2017-01-25 | P |
| B | 11 | 2017-01-30 | R |
| C | 13 | 2017-01-26 | Q |
| D | 23 | 2017-01-29 | Q |
+------+----+-------------+------+

You can also use Vertica's enhanced LIMIT clause:
WITH
-- input, don't use in real query
input(Name,ID,payment_date,Pack) AS (
SELECT 'A',11,DATE '31-Jan-2017','P'
UNION ALL SELECT 'C',13,DATE '31-Jan-2017','Q'
UNION ALL SELECT 'B',2, DATE '31-Jan-2017','R'
UNION ALL SELECT 'C',3, DATE '28-Jan-2017','P'
UNION ALL SELECT 'D',23,DATE '29-Jan-2017','Q'
UNION ALL SELECT 'B',11,DATE '30-Jan-2017','R'
UNION ALL SELECT 'A',17,DATE '25-Jan-2017','P'
UNION ALL SELECT 'C',13,DATE '26-Jan-2017','Q'
UNION ALL SELECT 'D',17,DATE '2-Feb-2017','R'
UNION ALL SELECT 'B',23,DATE '3-Feb-2017','P'
UNION ALL SELECT 'A',45,DATE '4-Feb-2017','Q'
UNION ALL SELECT 'B',3, DATE '5-Feb-2017','R'
)
-- end of input , start real query here:
SELECT * FROM input
LIMIT 1 OVER(PARTITION BY Name ORDER BY payment_date)
;
Happy playing ...
Marco the Sane

Related

List only repeating names

| personid | first | last | section |
| 1 | Jon | A | y3 |
| 2 | Bob | Z | t6 |
| 3 | Pat | G | h4 |
| 4 | Ron | Z | u3 |
| 5 | Sam | D | y3 |
| 6 | Sam | D | u3 |
| 7 | Pam | F | h4 |
I want to isolate all the repeat names, despite the other columns, like this:
| personid | first | last | section |
| 5 | Sam | D | y3 |
| 6 | Sam | D | u3 |
This is what I came up with but I cannot get it to work:
SELECT personid, last, first, section FROM d 01 WHERE EXISTS
(SELECT * FROM d 02 WHERE 02.last = 01.last AND 02.first = 01.first )
You could just do a window count and filter by that:
select personid, first, last, section
from (
select t.*, count(*) over(partition by first, last) cnt
from mytable t
) t
where cnt > 1
You must check that the 2 rows have different ids:
SELECT d1.personid, d1.last, d1.first, d1.section
FROM d d1 WHERE EXISTS (
SELECT *
FROM d d2
WHERE d1.personid <> d2.personid AND d2.last = d1.last AND d2.first = d1.first
)
Always qualify the column names with the table's name/alias and don't use numbers as aliases unless they are enclosed in backticks or square brackets.
See the demo.
Results:
| personid | last | first | section |
| -------- | ---- | ----- | ------- |
| 5 | D | Sam | y3 |
| 6 | D | Sam | u3 |
Another way to yield the same results as the other accepted answer:
SELECT personid,
A.firstName,
A.lastName,
section
FROM personTable as A
INNER JOIN (
SELECT
firstName,
lastName,
CASE
WHEN COUNT(*)>1 THEN 'Yes'
ELSE 'Null' , AS UseName
FROM
personTable
WHERE UseName='Yes') as B
ON A.firstName=B.firstName AND A.lastName=B.lastName
This solution subqueries itself. Since it is an inner join, it will only pull the values that join onto the subquery. Since I filtered anything with a count less than 2 out, only the duplicates will match.

SQL Count In Range

How could I count data in range which could be configured
Something like this,
CAR_AVBL
+--------+-----------+
| CAR_ID | DATE_AVBL |
+--------------------|
| JJ01 | 1 |
| JJ02 | 1 |
| JJ03 | 3 |
| JJ04 | 10 |
| JJ05 | 13 |
| JJ06 | 4 |
| JJ07 | 10 |
| JJ08 | 1 |
| JJ09 | 23 |
| JJ10 | 11 |
| JJ11 | 20 |
| JJ12 | 3 |
| JJ13 | 19 |
| JJ14 | 22 |
| JJ15 | 7 |
+--------------------+
ZONE_CFG
+--------+------------+
| DATE | ZONE_DESCR |
+--------+------------+
| 15 | GREEN_ZONE |
| 25 | YELLOW_ZONE|
| 30 | RED_ZONE |
+--------+------------+
Table ZONE_CFG is configurable, so I could not use static value for this
The DATE column mean maximum date for each ZONE
And the result what I expected :
+------------+----------+
| ZONE_DESCR | AVBL_CAR |
+------------+----------+
| GREEN_ZONE | 11 |
| YELLOW_ZONE| 4 |
| RED_ZONE | 0 |
+------------+----------+
Please could someone help me with this
You can use LAG and group by as following:
SELECT
ZC.ZONE_DESCR,
COUNT(1) AS AVBL_CAR
FROM
CAR_AVBL CA
JOIN ( SELECT
ZONE_DECR,
COALESCE(LAG(DATE) OVER(ORDER BY DATE) + 1, 0) AS START_DATE,
DATE AS END_DATE
FROM ZONE_CFG ) ZC
ON ( CA.DATE_AVBL BETWEEN ZC.START_DATE AND ZC.END_DATE )
GROUP BY
ZC.ZONE_DESCR;
Note: Don't use oracle preserved keywords (DATE, in your case) as the name of the columns. Try to change it to something like DATE_ or DATE_START or etc..
Cheers!!
If you want the zero 0, I might suggest a correlated subquery instead:
select z.*,
(select count(*)
from car_avbl c
where c.date_avbl >= start_date and
c.date_avbl <= date
) as avbl_car
from (select z.*,
lag(date, 1, 0) as start_date
from zone_cfg z
) z;
In Oracle 12C, can phrase this using a lateral join:
select z.*,
(c.cnt - lag(c.cnt, 1, 0) over (order by z.date)) as cnt
from zone_cfg z left join lateral
(select count(*) as cnt
from avbl_car c
where c.date_avbl <= z.date
) c
on 1=1

select rows based on equal columns values

consider we have a table with this columns
Id
fk_newsId
fk_NewsGroupId
fk_NewsZoneId
I need to select all records with same fk_NewsGroup and fk_NewsZone
something like this
+----+-----------+--------------+-------------+
| Id | fk_NewsId | fk_NewsGroup | fk_NewsZone |
+----+-----------+--------------+-------------+
| 1 | 60 | 5 | 8 |
| 2 | 30 | 5 | 8 |
| 3 | 31 | 9 | 20 |
| 4 | 5 | 9 | 20 |
| 5 | 12 | 9 | 20 |
| 6 | 1000 | 20 | 11 |
| 7 | 21 | 20 | 11 |
| 8 | 6 | 20 | 11 |
+----+-----------+--------------+-------------+
how can do that?
I tride group by like this
but it dosnt give desired output
select fk_NewsId, fk_NewsGroup,fk_NewsZone from tbl_test
group by fk_NewsGroup,fk_NewsZone,fk_NewsId
You can try to use COUNT with window function, to get the count by fk_NewsGroup and fk_NewsZone columns.
then get count greater than one.
SELECT *
FROM (
SELECT *,COUNT(*) OVER(PARTITION BY fk_NewsGroup,fk_NewsZone ORDER BY fk_NewsZone) cnt
FROM tbl_test
)t1
where t1.cnt > 1
dbfiddle
Not absolutely clear as to what you mean, but something like so:
SELECT t.Id, t.fk_NewsId, t.fk_NewsGroup, t.fk_NewsZone FROM tbl_test t
INNER JOIN (
SELECT fk_NewsGroup,fk_NewsZone, COUNT(*) AS Counted FROM tbl_test
GROUP BY fk_NewsGroup,fk_NewsZone
HAVING COUNT(*) > 1) g
ON t.fk_NewsGroup = g.fk_NewsGroup
AND t.fk_NewsZone = g.fk_NewsZone
DBFiddle example
I would use Group by and do it like:
select max(id) as Id, Max(fk_NewsId) as fk_NewsId, fk_NewsGroup,fk_NewsZone from #temp
group by fk_NewsGroup,fk_NewsZone

SQL - Rows that are repetitive with a particular condition

We have a table like this:
+----+-------+-----------------+----------------+-----------------+----------------+-----------------+
| ID | Name | RecievedService | FirstZoneTeeth | SecondZoneTeeth | ThirdZoneTeeth | FourthZoneTeeth |
+----+-------+-----------------+----------------+-----------------+----------------+-----------------+
| 1 | John | SomeService1 | 13 | | 4 | |
+----+-------+-----------------+----------------+-----------------+----------------+-----------------+
| 2 | John | SomeService1 | 34 | | | |
+----+-------+-----------------+----------------+-----------------+----------------+-----------------+
| 3 | Steve | SomeService3 | | | | 2 |
+----+-------+-----------------+----------------+-----------------+----------------+-----------------+
| 4 | Steve | SomeService4 | | | | 12 |
+----+-------+-----------------+----------------+-----------------+----------------+-----------------+
Every digit in zones is a tooth (dental science) and it means "John" has got "SomeService1" twice for tooth #3.
+----+------+-----------------+----------------+-----------------+----------------+-----------------+
| ID | Name | RecievedService | FirstZoneTeeth | SecondZoneTeeth | ThirdZoneTeeth | FourthZoneTeeth |
+----+------+-----------------+----------------+-----------------+----------------+-----------------+
| 1 | John | SomeService1 | 13 | | 4 | |
+----+------+-----------------+----------------+-----------------+----------------+-----------------+
| 2 | John | SomeService1 | 34 | | | |
+----+------+-----------------+----------------+-----------------+----------------+-----------------+
Note that Steve has received services twice for tooth #2 (4th Zone) but services are not one.
I'd write some code that gives me a table with duplicate rows (Checking the only patient and received service)(using "group by" clause") but I need to check zones too.
I've tried this:
select ROW_NUMBER() over(order by vv.ID_sick) as RowNum,
bb.Radif,
bb.VCount as 'Count',
vv.ID_sick 'ID_Sick',
vv.ID_service 'ID_Service',
sick.FNamesick + ' ' + sick.LNamesick as 'Sick',
serv.NameService as 'Service',
vv.Mab_Service as 'MabService',
vv.Mab_daryafti as 'MabDaryafti',
vv.datevisit as 'DateVisit',
vv.Zone1,
vv.Zone2,
vv.Zone3,
vv.Zone4,
vv.ID_dentist as 'ID_Dentist',
dent.FNamedentist + ' ' + dent.LNamedentist as 'Dentist',
vv.id_do as 'ID_Do',
do.FNamedentist + ' ' + do.LNamedentist as 'Do'
from visiting vv inner join (
select ROW_NUMBER() OVER(ORDER BY a.ID_sick ASC) AS Radif,
count(a.ID_sick) as VCount,
a.ID_sick,
a.ID_service
from visiting a
group by a.ID_sick, a.ID_service, a.Zone1, a.Zone2, a.Zone3, a.Zone4
having count(a.ID_sick)>1)bb
on vv.ID_sick = bb.ID_sick and vv.ID_service = bb.ID_service
left join InfoSick sick on vv.ID_sick = sick.IDsick
left join infoService serv on vv.ID_service = serv.IDService
left join Infodentist dent on vv.ID_dentist = dent.IDdentist
left join infodentist do on vv.id_do = do.IDdentist
order by bb.ID_sick, bb.ID_service,vv.datevisit
But this code only returns rows with all tooths repeated. What I want is even one tooth repeats ...
How can I implement it?
I need to check characters in zones.
**Zone's datatype is varchar
This is a bad datamodel for what you are trying to do. By storing the teeth as a varchar, you have kind of decided that you are not interested in single teeth, but only in the group of teeth. Now, however, you are trying to investigate on single teeth.
You'd want a datamodel like this:
service
+------------+--------+-----------------+
| service_id | Name | RecievedService |
+------------+--------+-----------------+
| 1 | John | SomeService1 |
+------------+--------+-----------------+
| 3 | Steve | SomeService3 |
+------------+--------+-----------------+
| 4 | Steve | SomeService4 |
+------------+-------+-----------------+
service_detail
+------------+------+-------+
| service_id | zone | tooth |
+------------+------+-------+
| 1 | 1 | 1 |
| 1 | 1 | 3 |
| 1 | 3 | 4 |
+------------+------+-------+
| 1 | 1 | 3 |
| 1 | 1 | 4 |
+------------+------+-------+
| 3 | 4 | 2 |
+------------+------+-------+
| 4 | 4 | 1 |
| 4 | 4 | 2 |
+------------+------+-------+
What you can do with the given datamodel is to create such table on-the-fly using a recursive query and string manipulation:
with unpivoted(service_id, name, zone, teeth) as
(
select recievedservice, name, 1, firstzoneteeth
from mytable where len(firstzoneteeth) > 0
union all
select recievedservice, name, 2, secondzoneteeth
from mytable where len(secondzoneteeth) > 0
union all
select recievedservice, name, 3, thirdzoneteeth
from mytable where len(thirdzoneteeth) > 0
union all
select recievedservice, name, 4, fourthzoneteeth
from mytable where len(fourthzoneteeth) > 0
)
, service_details(service_id, name, zone, tooth, teeth) as
(
select
service_id, name, zone, substring(teeth, 1, 1), substring(teeth, 2, 10000)
from unpivoted
union all
select
service_id, name, zone, substring(teeth, 1, 1), substring(teeth, 2, 10000)
from service_details
where len(teeth) > 0
)
, duplicates(service_id, name) as
(
select distinct service_id, name
from service_details
group by service_id, name, zone, tooth
having count(*) > 1
)
select m.*
from mytable m
join duplicates d on d.service_id = m.recievedservice and d.name = m.name;
A lot of work and a rather slow query due to a bad datamodel, but still feasable.
Rextester demo: http://rextester.com/JVWK49901

Split the date of same column in multiple rows till the next date value is specified - SQL Server

I have this table
+------+------------+-----+
| Code | date | qty |
+------+------------+-----+
| 1 | 06-07-2017 | 44 |
| 1 | 08-07-2017 | 45 |
| 2 | 07-07-2017 | 32 |
| 2 | 09-07-2017 | 33 |
+------+------------+-----+
and I want to display it this way
+------+------------+-----+
| Code | date | qty |
+------+------------+-----+
| 1 | 06-07-2017 | 44 |
| 1 | 07-07-2017 | 44 |
| 1 | 08-07-2017 | 45 |
| 2 | 07-07-2017 | 32 |
| 2 | 08-07-2017 | 32 |
| 2 | 09-07-2017 | 33 |
+------+------------+-----+
I want to split the date of same 'Code' and keep the same value for 'qty' till the next date of same 'Code'.
You need a calendar table and Outer Apply
;WITH cte
AS (SELECT Min([date]) AS st,
Max([date]) ed,
code
FROM Yourtable
GROUP BY code
UNION ALL
SELECT Dateadd(dd, 1, st) AS st,
ed,
code
FROM cte
WHERE Dateadd(dd, 1, st) <= ed)
SELECT c.code,
[date]=c.st,
qty
FROM cte c
OUTER apply (SELECT TOP 1 qty
FROM Yourtable a
WHERE a.code = c.code
AND c.st >= a.[date]
ORDER BY [date] DESC) oa
ORDER BY c.code,st
Note : For the sake of completeness I have used Recursive CTE to generate the dates you can always create a physical calendar table in your database and use it.
Live Demo