SQL - Subquery Error and Sum Error - mysql

The SQL code I have is shown below. The issue I am having is with the subquery first of all.
The result displays the error:
SQL ERROR: Subquery returned more than 1 value. This is not permitted
when the subquery follows =, !=, <, <= , >, >= or when the subquery is
used as an expression
Need to show rows with date greater than (or equal to) the date where ESource = Detail - The rows need to be summed in some columns and a single value selected in others.
Code used:
select DISTINCT
A.Policy,
A.Fund,
(SUM(A.AddUnits)) AS SUM,
((C.TotalUnits - (SUM(A.AddUnits))) * A.Price) AS Value,
Inner JOIN TableC C
ON C.PolicyNumber = A.PolicyNumber
where A.PolicyNumber = '120' AND C.NumberOfUnits > 0 AND C.InvestmentFund = A.InvestmentFund
AND A.DateOfEntry < DATEADD(year, -1, GetDate())
AND A.DateOfEntry >= (Select DateOfEntry FROM TableA AS D where D.ESource = 'Detail')
AND A.UnitPrice = (Select UnitPrice FROM TableA AS E where E.ESource = 'Detail')
ORDER BY IH.DateOfEntry DESC
Tables are:
Table A:
Policy Fund Units Price ESource Date
120 BR 6 0.74 RE 2015
120 BR -100 0.72 Detail 2014
120 BR 6 0.71 RE 2013
TABLE C:
Policy Fund TotalUnits
120 BR 400
DESIRED RESULT:
Policy Fund Sum Price Value
120 BR [6+(-100)] = -94 0.72 [(400+(-94))*0.72] = 220.32
As well as the sub query issues - the command to get price = 0.72 [where ="Detail"] is stopping the sum of both rows occurring making Sum = -100 and not -94
Any help with the errors would be greatly appreciated

The problem is in your where clause:
where A.PolicyNumber = '120' AND
C.NumberOfUnits > 0 AND
C.InvestmentFund = A.InvestmentFund AND
A.DateOfEntry < DATEADD(year, -1, GetDate()) AND
A.DateOfEntry >= (Select DateOfEntry FROM TableA AS D where D.ESource = 'Detail') AND
A.UnitPrice = (Select UnitPrice FROM TableA AS E where E.ESource = 'Detail')
The last two clauses are the problem. One way to fix this is with the keywords ANY, SOME, or ALL:
where A.PolicyNumber = '120' AND
C.NumberOfUnits > 0 AND
C.InvestmentFund = A.InvestmentFund AND
A.DateOfEntry < DATEADD(year, -1, GetDate()) AND
A.DateOfEntry >= ALL (Select DateOfEntry FROM TableA AS D where D.ESource = 'Detail') AND
A.UnitPrice = ALL (Select UnitPrice FROM TableA AS E where E.ESource = 'Detail')
However, I prefer explicit aggregation:
where A.PolicyNumber = '120' AND
C.NumberOfUnits > 0 AND
C.InvestmentFund = A.InvestmentFund AND
A.DateOfEntry < DATEADD(year, -1, GetDate()) AND
A.DateOfEntry >= (Select MAX(DateOfEntry) FROM TableA AS D where D.ESource = 'Detail') AND
A.UnitPrice = (Select MAX(UnitPrice) FROM TableA AS E where E.ESource = 'Detail')
Note that the query in your question seems to be missing a from clause. And the condition C.InvestmentFund = A.InvestmentFun should be in an on clause not in the where clause.

Related

Script is freezing. Want to take the average count of "Women" for 1 month ago to 60 months ago. This portion of the code is freezing on me

/*This is the code Im using to count and take the Average?*/
`SELECT GETDATE() AS CurrentDate,
(SELECT COUNT(1) FROM People WITH(NOLOCK) LEFT JOIN LinkPeopleToCompanies WITH(NOLOCK) ON
People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE() OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE() - 2190
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID) AS TotalDirectorsAddedin6years,
(SELECT AVG(DATEDIFF("yyyy",People.BirthDay,GETDATE())) FROM People WITH(NOLOCK) LEFT JOIN
LinkPeopleToCompanies WITH(NOLOCK) ON People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE() - 30 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE() - 30
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID) AS AverageDirectorAge1MonthAgo,
/I repeat this same code going back progressively by 30 days or 1 month. Need some more effective code/
(SELECT AVG(LinkPeopleToCompanies.CustomInt1) FROM People WITH(NOLOCK) LEFT JOIN
LinkPeopleToCompanies WITH(NOLOCK) ON People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE() - 30 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE() - 30
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID) AS AverageDirectorTenure1MonthAgo,
TotalWomenonBoard1 = CASE WHEN ((SELECT COUNT(1) FROM People WITH(NOLOCK) INNER JOIN
LinkPeopleToCompanies WITH(NOLOCK) ON People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE()- 30 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE()- 30
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID) = 0 )
THEN '9999999'
ELSE (SELECT COUNT(1) FROM People WITH(NOLOCK) INNER JOIN LinkPeopleToCompanies WITH(NOLOCK) ON
People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE() - 30 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE() - 30
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID
AND People.Gender = 'F')
END,
TotalWomenonBoard2 = CASE WHEN ((SELECT COUNT(1) FROM People WITH(NOLOCK) INNER JOIN
LinkPeopleToCompanies WITH(NOLOCK) ON People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE()- 60 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE()- 60
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID) = 0 )
THEN '9999999'
ELSE (SELECT COUNT(1) FROM People WITH(NOLOCK) INNER JOIN LinkPeopleToCompanies WITH(NOLOCK) ON
People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE() - 60 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE() - 60
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID
AND People.Gender = 'F')
END,
TotalWomenonBoard60 = CASE WHEN ((SELECT COUNT(1) FROM People WITH(NOLOCK) INNER JOIN
LinkPeopleToCompanies WITH(NOLOCK) ON People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE()- 1825 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE()- 1825
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID) = 0 )
THEN '9999999'
ELSE (SELECT COUNT(1) FROM People WITH(NOLOCK) INNER JOIN LinkPeopleToCompanies WITH(NOLOCK) ON
People.PeopleID = LinkPeopleToCompanies.PeopleID
WHERE (LinkPeopleToCompanies.ToDate >= GETDATE()- 1825 OR LinkPeopleToCompanies.ToDate IS NULL)
AND LinkPeopleToCompanies.SinceDate <= GETDATE()- 1825
AND LinkPeopleToCompanies.CompaniesID = Companies.CompaniesID
AND People.Gender = 'F')
END
FROM Companies WITH(NOLOCK)
ORDER BY Company`
Syntax in the question appears to be Transact-SQL ala Microsoft SQL Server (which is different than MySQL and Oracle; this question seems to be unrelated to the actual client program, that detail might be important... setting that aside.
For performance, I would opt to use conditional aggregation. I would use a single SELECT and one set of table references, and avoid multiple SELECT queries against the same tables with different WHERE clauses.
To get a count of "some" rows, I'd use a conditional in an expression to return a 1 or 0, and then total those up with SUM. (Or, I return a non-null and NULL, and use COUNT.)
Without digging into every SELECT ... FROM included in the question, it looks like the only form of statement we need is this:
SELECT GETDATE() AS CurrentDate
, SUM(CASE WHEN ... THEN 1 ELSE 0 END) AS stat_count_foo
FROM Companies c
LEFT
JOIN LinkPeopleToCompanies t
ON t.companiesid = c.companiesid
LEFT
JOIN People p
ON p.peopleid = t.peopleid
GROUP
BY c.companiesid
Note that there is not a WHERE clause in there. (We would only include conditions in the WHERE clause to exclude rows that we don't need for any metrics.)
Adding some example metrics, our statement would look something like this:
SELECT GETDATE() AS CurrentDate
, SUM( CASE
WHEN (t.todate >= GETDATE() OR t.todate IS NULL)
AND t.sincedate <= GETDATE() - 2190
THEN 1
ELSE 0
END
) AS TotalDirectorsAddedin6years
, AVG( CASE
WHEN (t.todate >= GETDATE() - 30 OR t.todate IS NULL)
AND t.sincedate <= GETDATE() - 30
THEN DATEDIFF('yyyy',p.birthDay,GETDATE())
ELSE NULL
END
) AS AverageDirectorAge1MonthAgo
, SUM( CASE
WHEN (t.todate >= GETDATE()- 1825 OR t.todate IS NULL)
AND t.sincedate <= GETDATE()- 1825
AND p.Gender = 'F'
THEN 1
ELSE 0
END
) AS TotalWomenonBoard60
FROM Companies c
LEFT
JOIN LinkPeopleToCompanies t
ON t.companiesid = c.companiesid
LEFT
JOIN People p
ON p.peopleid = t.peopleid
GROUP
BY c.companiesid
Extend this by adding additional expressions. Do NOT add any additional SELECT, FROM or JOIN keywords. We could wrap the aggregate expressions in another expression to replace a NULL value with a "0" (for a company that doesn't have any people linked to it.

MYSQL Query Select with condition & Join

I have these 3 tables ( with these structure):
outreach
id url profile_id
------------------------------------------
40 www.google.com 2
41 www.yahoo.com 3
42 www.test.com 1
outreach_links
id outreach_id end_date status
-----------------------------------------------
1 41 2016-01-12 Pending
2 40 2016-03-12 Pending
3 40 2016-02-12 Approved
comments
id outreach_id name
----------------------------
1 40
2 40
3 40
and I have this Query:
select o.*,
SUM(if(ol.status = "Approved" and (ol.end_date > now() or end_date is null), 1, 0)) as cond1,
SUM(if(ol.status = "Pending" and (ol.end_date != now() or end_date is null), 1, 0)) as cond2,
SUM(if(ol.status = "Pending" and (ol.end_date < now()), 1, 0)) as cond3
from outreach o
left join outreach_links ol on ol.outreach_id = o.id
where o.profile_id=2
group by o.id
having (cond1 = 0 and cond2 = 0) or (cond1 = 0 and (cond2 = 1 and cond3 >=1)) order by ol.end_date desc
I am trying to fix this Query and make it also select the following:
1). ol.* ONLY if MAX(end_date) and
2). Count(id.comment) count all comments for that particular row
is that possible?
right now here is the output
+"id": "40"
+"profile_id": "2"
+"url": "http://www.google.com"
+"created_at": "2016-12-05 21:55:10"
+"updated_at": "2016-12-05 22:49:56"
+"cond1": "0"
+"cond2": "0"
+"cond3": "5"
I want to add
+"max_date": get me max of end_date and the whole row of the row highlighted
+"Count(comments)": get me all the comments count for this one which is 3
Thanks
Are you trying to get the latest update date? The following query should give you the latest updated date.
However, I do not understand what you are trying to get for cond1, cond2, cond3, and what should be populated as created_date, and updated_date? Can you please give definitions for these fields?
SELECT o.*, ol.*, COUNT(c.id)
FROM outreach o
LEFT JOIN outreach_links ol ON ol.outreach_id = o.id
LEFT JOIN comments c ON c.outreach_id = o.id
WHERE ol.id = (SELECT ol2.id
FROM outreach_links ol2
WHERE ol2.outreach_id = ol.outreach_id
ORDER BY ol2.end_date, ol2.id DESC
LIMIT 1)
OR ol.id IS NULL
GROUP BY o.id, ol.id

Limit to two results for each individual ID

How would I return two results for each individual meeting.id?
I've tried things like Row_Count() and Rank() but they seem to cause syntax errors.
This is my query which I need adapting to show the two results per meeting.id.
SELECT meeting_appointment.* FROM `meeting`
INNER JOIN meeting_appointment ON (
meeting_appointment.meeting_id = meeting.id AND meeting_appointment.pupil_id = 0 AND meeting_appointment.guardian_id = 0 AND meeting_appointment.deleted = 0
)
WHERE (
meeting.grade_id = "-1" OR meeting.grade_id IN ('87')
)
AND meeting.startTime < '2016-10-06 14:00:00' + INTERVAL 1 HOUR AND meeting.startTime > '2016-10-06 14:00:00' - INTERVAL 1 HOUR
GROUP by meeting_appointment.id
ORDER BY meeting_appointment.startTime ASC
If your meeting_appointment.id are consecutive then this should show the first two
meeting_appointment.id per meeting.id:
SELECT meeting_appointment.* FROM `meeting`
INNER JOIN meeting_appointment ON (meeting_appointment.meeting_id = meeting.id)
WHERE (meeting.grade_id = "-1" OR meeting.grade_id IN ('87'))
AND meeting.startTime < '2016-10-06 14:00:00' + INTERVAL 1 HOUR AND meeting.startTime > '2016-10-06 14:00:00' - INTERVAL 1 HOUR
AND meeting_appointment.pupil_id = 0
AND meeting_appointment.guardian_id = 0
AND meeting_appointment.deleted = 0
AND NOT EXISTS
(SELECT 1 FROM meeting_appointment ma WHERE ma.id<meeting_appointment.id-1
AND ma.pupil_id = 0
AND ma.guardian_id = 0
AND ma.deleted = 0
)
GROUP by meeting_appointment.id
ORDER BY meeting_appointment.startTime ASC
If you meeting_appointment.id are just random or non-numeric then it might need some tweaks but in order to code those teaks we'll need to know a bit more about the schema.
You don't have an aggregate function, so GROUP BY is redundant. Remove that, and add a LIMIT clause, and you should be good to go:
SELECT ma.*
FROM meeting m
INNER JOIN meeting_appointment ma ON ma.meeting_id = m.id
WHERE m.grade_id IN ('-1','87')
AND ma.pupil_id = 0
AND ma.guardian_id = 0
AND ma.deleted = 0
AND m.startTime < '2016-10-06 14:00:00' + INTERVAL 1 HOUR
AND m.startTime > '2016-10-06 14:00:00' - INTERVAL 1 HOUR
ORDER BY ma.startTime ASC
LIMIT 2;
You could also use BETWEEN for the datetime column.

MySQL update query with average from last 1 year

I am trying to update values from another table by taking averages for the last 1 year values. the query i have written below is definitely wrong as AsOfDate has not been included in query for table b, but is indicative of what i am trying to do. Thanks in advance.
update MT_1YR_AVG_EXGRATE a
join (select avg(ExchangeRate) as ExchangeRate, CurrencyId from Currency_Exchange_Rate_Data
where BaseCurrencyId = "USD" group by CurrencyId) b
on b.CurrencyId = a.CurrencyId and b.AsOfDate <= a.AsOfDate and b.AsOfDate > date_sub(a.AsOfDate,interval 1 year)
set a.ExchangeRate = b.ExchangeRate
where a.ExchangeRate = null;
Solved:
update MT_1YR_AVG_EXGRATE c join (
select b.CurrencyId, b.AsOfDate, avg(a.ExchangeRate) as ExchangeRate from Currency_Exchange_Rate_Data a, MT_1YR_AVG_EXGRATE b
where a.BaseCurrencyId = "USD" and a.CurrencyId = b.CurrencyId and b.ExchangeRate is NULL
and a.AsOfDate <= b.AsOfDate and a.AsOfDate > date_sub(b.AsOfDate,interval 1 year)
group by b.CurrencyId, b.AsOfDate) d
on d.CurrencyId = c.CurrencyId and d.AsOfDate = c.AsOfDate
set c.ExchangeRate = cast(d.ExchangeRate as decimal(19,5))
where c.ExchangeRate is NULL;

SQL Server 2008 left join where clause based on max date

Got some help the other day on this and now really want to make a change for the better
Here is code
select
machines.serialnum, DRPS.assetnumber, DRPS.MOCALCSUM, DRPS.MICALCSUM,
DRPS.COCALCSUM, DRPS.CICALCSUM, ISSUED.TotalIssued,
REDEEMED.TotalRedeemed,drps.dropdate1,drps.dropdate2
from
(select serialnum
from machineinfo) as machines
LEFT JOIN
(select
assetnumber, min(dropdate) as [dropdate1], max(dropdate) as [dropdate2],
sum(mocalc) AS [MOCALCSUM], sum(micalc) AS [MICALCSUM],
sum(cocalc) AS [COCALCSUM],sum(cicalc) AS [CICALCSUM]
from drops
where dropdate > '09/04/2012' and dropdate < dateadd(hour, -0, getdate())
GROUP BY assetnumber) AS DRPS on machines.serialnum = drps.assetnumber
LEFT JOIN
(select
snissued, cast(sum(amount) as money) / 100 AS [TotalIssued]
from tickets
where dateissued > '09/04/2012' and dateissued < dateadd(hour, 0, getdate())
group by snissued) AS ISSUED ON machines.serialnum = ISSUED.snissued
LEFT JOIN
(select
snredeemed, cast(sum(amount) as money) / 100 AS [TotalRedeemed]
from tickets
where dateredeemed > '09/04/2012' and dateredeemed < dateadd(hour, 0, getdate())
group by snredeemed) AS REDEEMED ON machines.serialnum = REDEEMED.snredeemed
what I would like to accomplish if possible is for the second and third join is to use the drps.drop1 in the where clause like this
where dropdate > drps.dropdate1 and dropdate < drps.dropdate2
but it does not work
here is working output
serialnum MOCALCSUM MICALCSUM COCALCSUM CICALCSUM TotalIssued TotalRedeemed dropdate1 dropdate2
0-2739-41401 5482 5498 132 148 3258.00 3110.00 2012-09-04 13:36:53.450 2012-09-05 13:55:38.750
0-2459-36182 1110 1054 114 58 1895.00 1657.00 2012-09-04 15:01:19.973 2012-09-05 13:55:38.967
end result is I need total issued and total redeemed date range to be between the min and max date per serial number
declare #datebegin datetime
declare #dateend datetime
set #datebegin = '09/04/2012'
set #dateend = '09/08/2012'
select machines.serialnum, DRPS.MOCALCSUM,DRPS.MICALCSUM,DRPS.COCALCSUM,DRPS.CICALCSUM,ISSUED.TotalIssued,
REDEEMED.TotalRedeemed,drps.dropdate1,drps.dropdate2
from (select serialnum
from machineinfo) as machines
LEFT JOIN (select assetnumber,min(dropdate)as [dropdate1], max(dropdate)as [dropdate2], sum(mocalc) AS [MOCALCSUM],sum(micalc) AS [MICALCSUM],
sum(cocalc) AS [COCALCSUM],sum(cicalc) AS [CICALCSUM]
from drops
where dropdate > #datebegin and dropdate < #dateend AND (ignore is null)
group by assetnumber) AS DRPS
on machines.serialnum = drps.assetnumber
LEFT JOIN (select snissued,cast(sum(amount)as money)/100 AS [TotalIssued] from tickets
where (dateissued > (select min(dropdate)
from drops
where dropdate > #datebegin AND (ignore is null) and snissued = assetnumber ))
and
(dateissued < (select max(dropdate)
from drops
where dropdate < #dateend AND (ignore is null) and snissued = assetnumber))
group by snissued) AS ISSUED
ON machines.serialnum=ISSUED.snissued
LEFT JOIN (select snredeemed,cast(sum(amount)as money)/100 AS [TotalRedeemed]
from tickets
where (dateredeemed > (select min(dropdate)
from drops
where dropdate > #datebegin AND (ignore is null)and snissued = assetnumber))
and
(dateredeemed < (select max(dropdate)
from drops
where dropdate < #dateend AND (ignore is null)and snissued = assetnumber ))
group by snredeemed) AS REDEEMED
ON machines.serialnum=REDEEMED.snredeemed
order by REDEEMED.TotalRedeemed desc
based on the suggestion from comments i added the subquery into the selects and i am getting expected results now