Extract only the topline of data from a specific table SQL - sql

I'm having trouble extracting the topline of data from a table and joining it with other extracted fields from other tables.
I have 3 tables:
Person
Folder
Earnings
Person:
PERSONID |FORENAMES|SURNAME|DOB |GENDER|NINO
1000000 |JOHNSTON |ALI |10/10/80 |M |JK548754A
Folder:
FOLDERID|FOLDERREF
1000000 |104567LK
Earnings:
FOLDERID|DATESTARTED|DATEENDED |GROSSEARNINGS
1000000 |01-04-2014 |31-03-2015 |31846.00
1000000 |01-04-2013 |31-03-2014 |31160.04
1000000 |01-04-2012 |31-03-2013 |30011.04
1000000 |01-04-2011 |31-03-2012 |29123.94
I need my data to look like:
JOHNSTON |ALI| 10-10-1980 | 31-03-2015 | 31846.00 | 31649.60
I've tried:
SELECT A.PERSONID, A.SURNAME, A.FORENAMES, A.DOB, B.FOLDERREF, C.DATEENDED, C.GROSSEARNINGS, C.BASICEARNINGS, C.FLUCTUATINGEARNINGS
FROM PERSON A, FOLDER B, EARNINGS C
WHERE A.PERSONID = B.FOLDERID AND B.FOLDERID = C.FOLDERID
Which extracts all of the data from the EARNINGS table, but I only wish to extract the top line.
Any advice is greatly received.

If you want just the data from the latest date then you could do something like the query below. Bear in mind, you're using fields like c.BasicEarnings and c.FluctuatingEarnings that you don't have in table 'Earnings'
SELECT a.PersonID
,a.Suranme
,a.Forenames
,a.DOB
,b.FolderRef
,c.DateEnded
,c.GrossEarnings
FROM Person a
JOIN Folder b ON a.FolderID = b.FolderID
JOIN (
SELECT e.FolderID
,e.DateEnded
,e.GrossEarnings
FROM Earnings e
JOIN (
SELECT FolderID
,MAX(DateEnded) DateEnded
FROM Earnings
GROUP BY FolderID
) m ON e.FolderID = m.FolderID
AND e.DateEnded = m.DateEnded
) c ON a.FolderID = c.FolderID

Assuming the final field in your expected output is GROSSEARNINGS and by "I only wish to extract the top line" you mean latest (by date) then use GROUP BY with a MAX function.
SELECT p.FORENAMES, p.SURNAME, p.DOB, MAX(e.DATEENDED), e.GROSSEARNINGS, e.BASICEARNINGS
FROM Person p
INNER JOIN Earnings e ON p.PERSONID = e.FOLDERID
GROUP BY p.FORENAMES, p.SURNAME, p.DOB, e.GROSSEARNINGS, e.BASICEARNINGS

Related

How count same columns from few tables in one SQL query

I have five tables, TDetective, TMonths, TProduct1, TProduct2 and TProduct3. I want to have a result set with four columns:
first is Detective name
second is count - how many TProduct1 are assigned to this Detective in special months
third is count - how many TProduct2 are assigned to this Detective in special months
and fourth is count - how many TProduct3 are assigned to this Detective in special months.
Please help me.
TDetective
|id |
|Detective|
TMonths
|id |
|Months |
TProduct1
|id |
|RequestDay|
|Mobile|
|Operator|
|Subjects|
|OccurrenceMode|
|Detective|
|Months|
TProduct2
|id |
|RequestDay|
|Mobile|
|Operator|
|Subjects|
|OccurrenceMode|
|Detective|
|Months|
TProduct3
|id |
|RequestDay|
|Mobile|
|Operator|
|Subjects|
|OccurrenceMode|
|Detective|
|Months|
My SQL query looks like this:
select
TDetective.Detective,
count(TProduct1.id) as countOfDetectiveP1
count(TProduct2.id) as countOfDetectiveP2
count(TProduct3.id) as countOfDetectiveP3
from
TDetective
left outer join
TProduct1 on TDetective.Detective = TProduct1.Detective
where
TProduct1.Months in (select months from TMonths)
left outer join
TProduct2 on TDetective.Detective = TProduct2.Detective
where
TProduct2.Months in (select months from TMonths)
left outer join
TProduct3 on TDetective.Detective = TProduct3.Detective
where
TProduct3.Months in (select months from TMonths)
group by
Detective.Detective
order by
Detective
Many thanks
I would cross join the first two tables to get a row for every detective and month. Then use correlated subqueries to fill in the rest of the information:
select d.id, d.month,
(select count(*)
from product1 p1
where p1.detective = d.id and
p1.month = m.month
),
(select count(*)
from product2 p2
where p2.detective = d.id and
p2.month = m.month
),
(select count(*)
from product3 p3
where p3.detective = d.id and
p3.month = m.month
)
from detective d cross join
months m;
You can add a where clause to the outer query to filter for particular detectives or months.
select d.Detective,
(select count(*)
from P118 p1
where p1.detective = d.Detective and
p1.Months in (select months from TempMonths)
),
(select count(*)
from P119 p2
where p2.detective = d.Detective and
p2.Months in (select months from TempMonths)
),
(select count(*)
from P120 p3
where p3.detective = d.Detective and
p3.Months in (select months from TempMonths)
)
from Detective d
order by Detective

I want to concatenate the result of these these two select queries as four columns in one table

select
SUM (cp.TotalAmount) as totalPaymentamount,
lvl4.SubSubsidaryAccountName as account1
from
TBLCPVMaster cp,TBLLevel4 lvl4
where
cp.SubSubsidaryAccountId = lvl4.SubSubsidaryAccountCode
group by
lvl4.SubSubsidaryAccountName
select
SUM (cr.TotalAmount) as totalReciveamount,
lvl4_2.SubSubsidaryAccountName as account2
from
TBLCRVMaster cr, TBLLevel4 lvl4_2
where
cr.SubSubsidaryAccountId = lvl4_2.SubSubsidaryAccountCode
group by
lvl4_2.SubSubsidaryAccountName
The resultant table should have 4 columns...please help
Thanks in advance
You really should start using the join syntax from ANSI-92. It has been 25 years.
with Payments as
(
select SUM (cp.TotalAmount) as totalPaymentamount
, lvl4.SubSubsidaryAccountName as account1
from TBLCPVMaster cp
join TBLLevel4 lvl4 on cp.SubSubsidaryAccountId = lvl4.SubSubsidaryAccountCode
group by lvl4.SubSubsidaryAccountName
)
, Receipts as
(
select SUM (cr.TotalAmount) as totalReciveamount
, lvl4_2.SubSubsidaryAccountName as account2
from TBLCRVMaster cr
join TBLLevel4 lvl4_2 on cr.SubSubsidaryAccountId = lvl4_2.SubSubsidaryAccountCode
group by lvl4_2.SubSubsidaryAccountName
)
select p.totalPaymentAmount
, p.account1
, r.totalReciveAmount
, r.account2
from Payments p
cross join Receipts r
Im just taking the simplest understanding of what I think you are trying to achieve... get the total paid and total received for each account.
select
lvl4.SubSubsidaryAccountName, -- surely you only need name once?
SUM (cp.TotalAmount) as totalPaymentamount,
SUM (cr.TotalAmount) as totalReciveamount
from
TBLLevel4 lvl4
left join TBLCPVMaster cp
on cp.SubSubsidaryAccountId=lvl4.SubSubsidaryAccountCode
left join TBLCRVMaster cr
on cr.SubSubsidaryAccountId=lvl4_2.SubSubsidaryAccountCode
group by lvl4.SubSubsidaryAccountName

SQL Query returning multiple Duplicate Results

scenario : I have Three Tables(Prisoners,AddPaymentTransaction,WithdrawPaymentTransation)
Date in Tables : i have 1 row of prisoner with PrisonerID=5 and two rows in both other table,
i have wrote query to return there data if any prisoner have add some payment in there account or with draw any payment from there payment on same day or on different dates etc.
here is my query :
select at.PrisonerID ,at.Amount as AAmount,at.Date as ADate,wt.Amount as WAmount,wt.Date as WDate
from Prisoners p, AddPaymentTransaction at,WithdrawPaymentTransation wt
where p.PrisonerID=at.PrisonerID and p.PrisonerID=wt.PrisonerID and at.PrisonerID=wt.PrisonerID and at.PrisonerID=5
but it gives me 4 rows, 9 rows when i have 3 rows of data in each Table etc.
i want rows of data with out duplicate. any suggestions or help will be highly appreciated.
It looks like at.PrisonerID = wt.PrisonerID in your query might be what is causing all of the duplicates. I am guessing AddPaymentTransaction and WithdrawPaymentTransation should not be linked together. So, how about the following:
SELECT at.PrisonerID, at.Amount as AAmount, at.Date as ADate,
wt.Amount as WAmount, wt.Date as WDate
FROM Prisoners p
INNER JOIN AddPaymentTransaction at p.PrisonerID = at.PrisonerID
INNER JOIN WithdrawPaymentTransation wt ON p.PrisonerID = wt.PrisonerID
WHERE at.PrisonerID = 5
but this probably isn't going to give you exactly what you are looking for either. So maybe something like the following:
SELECT * FROM
(
SELECT p.PrisonerID, 'AddPayment' AS Type,
apt.Amount as TransAmount, apt.Date AS TransDate
FROM Prisoners p
INNER JOIN AddPaymentTransaction apt ON p.PrisonerID = apt.PrisonerID
WHERE apt.PrisonerID = 5
UNION
SELECT p.PrisonerID, 'WithdrawPayment' AS Type,
wt.Amount as TransAmount, wt.Date as TransDate
FROM Prisoners p
INNER JOIN WithdrawPaymentTransation wt ON p.PrisonerID = wt.PrisonerID
WHERE wt.PrisonerID = 5
) AS mq
ORDER BY mq.TransDate DESC

Get percentages of larger group

The query below is kind of an ugly one so I hope I've got it spaced well enough to make it readable. The query finds the percentage of people that visit a given hospital if they are from a certain area. For instance, if 100 people live in county X and 20 go to hospital A and 80 go to hospital B the query outputs. How the heck is this sort of thing done? Let me know if I need to document the query or whatever I can do to make it clearer.
hospital A 20
hospital B 80
The query below works exactly like I want it to, but it give me thinking: how could this be done for every county in my table?
select hospitalname, round(cast(counts as float)/cast(fayettestrokepop as float)*100,2)as percentSeen
from
(
SELECT tblHospitals.hospitalname, COUNT(tblHospitals.hospitalname) AS counts, tblStateCounties_1.countyName,
(SELECT COUNT(*) AS Expr1
FROM Patient INNER JOIN
tblStateCounties ON Patient.stateCode = tblStateCounties.stateCode AND Patient.countyCode = tblStateCounties.countyCode
WHERE (tblStateCounties.stateCode = '21') AND (tblStateCounties.countyName = 'fayette')) AS fayetteStrokePop
FROM Patient AS Patient_1 INNER JOIN
tblHospitals ON Patient_1.hospitalnpi = tblHospitals.hospitalnpi INNER JOIN
tblStateCounties AS tblStateCounties_1 ON Patient_1.stateCode = tblStateCounties_1.stateCode AND Patient_1.countyCode = tblStateCounties_1.countyCode
WHERE (tblStateCounties_1.stateCode = '21') AND (tblStateCounties_1.countyName = 'fayette')
GROUP BY tblHospitals.hospitalname, tblStateCounties_1.countyName
) as t
order by percentSeen desc
EDIT: sample data
The sample data below is without the outermost query (the as t order by part).
The countsInTheCounty column is the (select count(*)..) part after 'tblStateCounties_1.countyName'
hospitalName hospitalCounts countyName countsInTheCounty
st. james 23 X 300
st. jude 40 X 300
Now with the outer query we would get
st james 0.076 (23/300)
st. jude 0.1333 (40/300)
Here is my guess. You'll have to test against your data or provide proper DDL + sample data.
;WITH totalCounts AS
(
SELECT StateCode, countyCode, COUNT(*) AS totalcount
FROM dbo.Patient GROUP BY StateCode, countyCode
)
SELECT
h.hospitalName,
hospitalCounts = COUNT(p.hospitalnpi),
c.countyName,
countsInTheCounty = tc.totalCount,
percentseen = CONVERT(DECIMAL(5,2), COUNT(p.hospitalnpi)*100.0/tc.totalCount)
FROM
dbo.Patient AS p
INNER JOIN
dbo.tblHospitals AS h
ON p.hospitalnpi = h.hospitalnpi
INNER JOIN
totalCounts AS tc
ON p.StateCode = tc.StateCode
AND p.countyCode = tc.countyCode
INNER JOIN
dbo.tblStateCounties AS c
ON tc.StateCode = c.stateCode
AND tc.countyCode = c.countyCode
GROUP BY
h.hospitalname,
c.countyName,
tc.totalcount
ORDER BY
c.countyName,
percentseen DESC;

Combining multiple sources of data into a unified table

My company is working with 3 partners and each partner can have multiple brands. Each week, I get a dump of each brand's user list
which I store in a MySQL database with a table for each brand. Each brand contains a list of users and some basic information
(birthyear, zip code, gender). Some users can be signed up with different brands and each brand can have it's own set of data on a user.
For example, a user is signed up with Canvas and MNM. At Canvas, their profile looks like this:
ID GENDER BIRTHYEAR POSTCODE MODIFIED
94bafdb3e155d30349f1113a25c0714f M 1973 2800 2009-01-01 09:01:01
and at MNM, like this:
ID GENDER BIRTHYEAR POSTCODE MODIFIED
94bafdb3e155d30349f1113a25c0714f 1973 1000 2009-09-09 09:01:01
I'd like to create a view (or table - I'm not sure which is best) that would combine the two records using the most recent version of the data, but also letting me know where the data came from.
So the above two records would combine to:
ID GENDER G_DATE G_BRAND BIRTHYEAR B_DATE B_BRAND POSTCODE P_DATE P_BRAND
94bafdb3e155d30349f1113a25c0714f M 2009-01-01 09:01:01 Canvas 1973 2009-09-09 09:01:01 MNM 1000 2009-09-09 09:01:01 MNM
I'm imagining some convoluted series of unions and sub queries, but I'm not even really sure where to begin.
I've created a view that merges all of the tables
CREATE VIEW view_combine AS
SELECT ID, GENDER, MODIFIED as G_DATE, 'Canvas' as G_BRAND,
BIRTHYEAR, MODIFIED as B_DATE, 'Canvas' as B_BRAND,
POSTCODE, MODIFIED as P_DATE, 'Canvas' as P_BRAND FROM canvas
UNION ALL
SELECT ID, GENDER, MODIFIED as G_DATE, 'Een' as G_BRAND,
BIRTHYEAR, MODIFIED as B_DATE, 'Een' as B_BRAND,
POSTCODE, MODIFIED as P_DATE, 'Een' as P_BRAND FROM een
UNION ALL
SELECT ID, GENDER, MODIFIED as G_DATE, 'MNM' as G_BRAND,
BIRTHYEAR, MODIFIED as B_DATE, 'MNM' as B_BRAND,
POSTCODE, MODIFIED as P_DATE, 'MNM' as P_BRAND FROM mnm
and then I'm trying to perform selections on that, but I don't think it's the right direction.
SELECT v1.hashkey, ge.gender, ge.g_date, ge.g_brand,
bi.birthyear, bi.b_date, bi.b_brand,
pc.postcode, pc.p_date, pc.p_brand
FROM view1 v1
JOIN (
select g.hashkey, g.gender, g.g_date, g.g_brand
from view1 g
left join view1 g1 ON g.hashkey = g1.hashkey AND g.g_date < g1.g_date
WHERE g1.hashkey IS NULL
) ge ON ge.HASHKEY = v1.HASHKEY
JOIN (
select b.hashkey, b.birthyear, b.b_date, b.b_brand
from view1 b
left join view1 b1 ON b.hashkey = b1.hashkey AND b.b_date < b1.b_date
WHERE b1.hashkey IS NULL
) bi ON bi.HASHKEY = v1.HASHKEY
JOIN (
select p.hashkey, p.postcode, p.p_date, p.p_brand
from view1 p
left join view1 p1 ON p.hashkey = p1.hashkey AND p.p_date < p1.p_date
WHERE p1.hashkey IS NULL
) pc ON pc.HASHKEY = v1.HASHKEY
GROUP BY v1.hashkey
I've managed to solve this. Essentially, I needed to select on the view and then sub-select on the view to get the fields I wanted. I found that ordering on the date within the sub-select returned the values I needed.
SELECT v1.hashkey, ge.gender, ge.g_date, ge.g_brand,
bi.birthyear, bi.b_date, bi.b_brand,
pc.postcode, pc.p_date, pc.p_brand
FROM view_combine v1
JOIN (
select g.hashkey, g.gender, g.g_date, g.g_brand
from view_combine g
left join view_combine g1 ON g.hashkey = g1.hashkey AND g.g_date < g1.g_date and g1.gender is not null
WHERE g1.hashkey IS NULL
order by g.g_date
) ge ON ge.HASHKEY = v1.HASHKEY
JOIN (
select b.hashkey, b.birthyear, b.b_date, b.b_brand
from view_combine b
left join view_combine b1 ON b.hashkey = b1.hashkey AND b.b_date < b1.b_date and b1.birthyear is not null
WHERE b1.hashkey IS NULL
order by b.b_date
) bi ON bi.HASHKEY = v1.HASHKEY
JOIN (
select p.hashkey, p.postcode, p.p_date, p.p_brand
from view_combine p
left join view_combine p1 ON p.hashkey = p1.hashkey AND p.p_date < p1.p_date and p1.postcode is not null
WHERE p1.hashkey IS NULL
order by p.p_date
) pc ON pc.HASHKEY = v1.HASHKEY
GROUP BY v1.hashkey
I realize you've solved already, but as a secondary viewpoint, this is something that I would pre-process.
Given the data:
Partner 1 - UserA, Male, Null, 6300, 9/9/09
Partner 2 - UserA, Null, 1980, 2300, 9/10/09
When querying for UserA, you would most likely want a "Most Current Record":
UserA, Male, 1980, 2300
Using the following tables:
Partner
TypeCode
DisplayName
CurrentUser
UserId
Gender
GenderSourcePartner
BirthYear
BirthYearSourcePartner
PostalCode
PostalCodeSourcePartner
PartnerSourceData
PartnerTypeCode
UserId
Gender
BirthYear
PostalCode
ModifiedDate
Then, when I receive the partner source files, I'd process it line by line to update the current user table and append to the PartnerSourceData table (using it as a log.)