I will attempt to explain my problem using a similar but simpler problem. Let's say that I'm writing software for a library, which has a table of Patrons and Books. In addition, it has a CheckOuts table which associates Patrons to any Books (1 per row) that they have checked out.
I'm using MSSQL 2005 and need to build a view or stored procedure that contains two columns, PatronID and HasBook, which needs to be dynamically generated: 1 if the patron has one or more book checked out, and 0 otherwise.
Here is my first attempt at writing a query to do this:
SELECT PatronID, MIN((SELECT 1, COUNT(BookID) FROM CheckOuts WHERE CheckOuts.PatronID = Patrons.PatronID)) AS HasBook
FROM Patrons
The error I'm receiving is:
Cannot perform an aggregate function
on an expression containing an
aggregate or a subquery.
I'm a very new SQL user, so if that query makes you cringe, I would sincerely appreciate any advice you would like to give. I'm very interested in the "right" way to do this.
Well, if you just want to know if the PatronId has any checkout, you can do the following:
SELECT PatronID, CASE WHEN B.PatronId IS NOT NULL THEN 1 ELSE 0 END AS HasBook
FROM Patrons A
LEFT JOIN (SELECT PatronId FROM CheckOuts GROUP BY PatronId) B
ON A.PatronId = B.PatronId
Try this:
SELECT a.PatronID, CASE b.PatronID WHEN NULL THEN 0 ELSE 1 END
FROM Patrons a
LEFT OUTER JOIN Patrons b ON b.PatronID = a.PatronID AND EXISTS (SELECT * FROM CheckOuts c WHERE c.PatronID = b.PatronID)
EDIT: Lamak's solution, using the derived table, may be better than mine. His is probably faster.
Related
My data is like this:
Desired output:
I have tried using following SQL:
CASE
WHEN (MINDAY_DIFF > 0) AND (MINDAY_DIFF IS NOT NULL)
THEN FIRST_VALUE(BP_MED) OVER (PARTITION BY ID ORDER BY MINDAY_DIFF ASC)
END AS DRUG
This returns NULL.
I also tried
CASE
WHEN (MINDAY_DIFF > 0)
THEN BP_MED
ELSE NULL
END AS DRUG
It returns both non-null values of BP_MED.
I also tried NVL but that didn't work either.
Since it is in Netezza. There are fewer solutions online. Please help.
The concept here is the following working inside out:
We didn't always have analytics to make things easier: So not knowing Netezza I took a more... antiquated approach. There may be better/more efficient ways; which I would look for given a place to play around with; but I think this would work in most any RDBMS as I tried to avoid any RDBMS Specific aspects unless we're dealing with pre left join supported RDBMS.
Find the Min ID and the day difference for that ID in a result set (MinAndID)
LEFT Join back to the baseSet to get all possible values specifically to get BP_Med
Then Join back to base Table to ensure we get ALL records and then populate only the BP_Med which links to the minDay_Diff. Since it's a left join only 1 record per ID should return.
UNTESTED:
SELECT A.*, DesiredDrug.BP_MED
FROM TABLE A
LEFT JOIN (SELECT ID, Fill_Date, BP_MED, MinDay_Diff
FROM TABLE BaseSet
INNER JOIN (SELECT MIN(MINDAY_DIFF) MDD, ID
FROM TABLE
WHERE ID is not null
GROUP BY ID) MinAndID
on BaseSet.ID = MinAndID.ID
and BaseSet.MinDay_Diff = MinAndID.MDD) DesiredDrug
on A.ID = DesiredDrug
and A.Fill_Date = DesiredDrug.Fill_Date
and A.BP_Med= DesiredDrug.BP_Med
and A.MinDay_Diff = DesiredDrug.MinDay_Diff
SELECT
c.ConfigurationID AS RealflowID, c.companyname,
c.companyphone, c.ContactEmail, COUNT(k.caseid)
FROM
dbo.Configuration c
INNER JOIN
dbo.cases k ON k.SiteID = c.ConfigurationId
WHERE
EXISTS (SELECT * FROM dbo.RepairEstimates
WHERE caseid = k.caseid)
AND c.AccountStatus = 'Active'
AND c.domainid = 46
GROUP BY
c.configurationid,c.companyname, c.companyphone, c.ContactEmail
I have this query - I am using the configuration table to get the siteid of the cases in the cases table. And if the case exists in the repair estimates table pull the company details listed and get a count of how many cases are in the repair estimator table for that siteid.
I hope that is clear enough of a description.
But the issue here is the count is not correct with the data that is being pulled. Is there something I could do differently? Different join? Remove the exists add another join? I am not sure I have tried many different things.
Realized I was using the wrong table. The query was correct.
Hello guys :) I have a table with 2 fields that are FK from another table. Those fields are idAdviceOwner and idAdviceNotifier. However, only one of them is with a value. I want to validate where or not it has a NULL value in order to choose the other field...Basically I've done this, of course is not running but that's the Idea... hope you guys help me out.
select *from notification n
inner join user u
on (n.idUserNotifier=u.idUser)
inner join advice a on (case WHEN n.idAdviceOwner IS NOT NULL
THEN n.idAdviceOwner=a.idAdvice
else n.idAdviceNotifier=a.idAdvice )
where n.idUser=8
select * from notification n
inner join user u
on (n.idUserNotifier=u.idUser)
inner join advice a on case WHEN n.idAdviceOwner IS NOT NULL
THEN n.idAdviceOwner else n.idAdviceNotifier END = a.idAdvice
where n.idUser=8;
Your idea was right but CASE-WHEN-ELSE-END returns an expression, not a condition. That's why you received the error.
You don't say what the error is, and I don't have a SQL engine handy to test on (I am on the road), but I think what you are trying to do here should basically work, except you probably need to do something along the lines of
... on a.idAdvice = (case WHEN n.idAdviceOwner IS NOT NULL
THEN n.idAdviceOwner
else n.idAdviceNotifier)
Another way is to join to advice twice in your query, once with idAdviceOwner, and once with idAdviceNotifier, and then use COALESCE or similar to combine the fields from the two results.
I have an SQL statement for a PICK sheet that returns the header/detail records for an order.
One of the fields in the SQL is basically a field to say if there are dangerous goods. If a single product on the order has a code against it, then the report should display that its hazardous.
The problem I am having is that in the SQL results, because I am putting the code on the report in the header section (and not the detail section), it is looking for the code only on the first row.
Is there a way through SQL to basically say "if one of these rows has this code, make all of these rows have this code"? I'm guessing a subselect would work here... the problem is, is that I am using a legacy system built on FoxPro and FoxPro SQL is terrible!
EDIT: just checked and I am running VFP8, subqueries in the SELECT statement were added in FVP9 :(
SELECT Header.HeaderId, Header.HeaderDescription,
Detail.DetailId, Detail.DetailDescription, Detail.Dangerous,
Danger.DangerousItems
FROM Header
INNER JOIN Detail ON Header.HeaderId = Detail.HeaderId
LEFT OUTER JOIN
(SELECT HeaderId, COUNT(*) AS DangerousItems FROM Detail WHERE Dangerous = 1 GROUP BY HeaderId) Danger ON Header.HeaderId = Danger.HeaderId
If Danger.DangerousItems > 0 then something is dangerous. If it is Null then nothing is dangerous.
If you can't do nested queries, then you should be able to create a view-like object (called a query in VFP8) for the nested select:
SELECT HeaderId, COUNT(*) AS DangerousItems FROM Detail WHERE Dangerous = 1 GROUP BY HeaderId
and then can you left join on that?
In VFP 8 and earlier, your best bet is to use three queries in a row:
SELECT Header.HeaderId, Header.HeaderDescription,
Detail.DetailId, Detail.DetailDescription, Detail.Dangerous,
Danger.DangerousItems
FROM Header
INNER JOIN Detail ON Header.HeaderId = Detail.HeaderId
INTO CURSOR csrDetail
SELECT HeaderId, COUNT(*) AS DangerousItems
FROM Detail
WHERE Dangerous
GROUP BY HeaderId
INTO CURSOR csrDanger
SELECT csrDetail.*, csrDanger.DangerousItems
FROM csrDetail.HeaderID = csrDanger.HeaderID
INTO CURSOR csrResult
I am joining two tables using SQL. I'm joining a table which contains charter flight information and a table which contains the crew assigned. In my results, I only want to display the rows that only have a value of "Pilot" in the the crew table and not "Copilot" or both.
SELECT * FROM TABLE_A JOIN TABLE_B ON (TABLE_A.Value = TABLE_B.Value) WHERE TABLE_A.OtherValue = 'Pilot'
This is off the top of my head, so some syntax may be off. The main point is the WHERE clause. You can specify the value that you are looking for in the column (in your case you are looking for Pilot).
EDIT: To prevent a value you can do something like WHERE TABLE.VALUE != 'Copilot' != may need to be written as <> depending on the what SQL it is.
EDIT2: My SQL-Server is throwing a hissy and not connecting, so this is also entirely off the top of my head and I think it's a bit of a hack-job, but I think it'll do the job. :)
SELECT [CHARTER].*, COUNT(*) as Tally FROM [CHARTER] JOIN [CREW] ON ([CHARTER].[CHAR_TRIP] = [CREW].[CHAR_TRIP]) WHERE [CREW].[CREW_JOB] = 'PILOT' OR [CREW].[CREW_JOB] = 'COPILOT' GROUP BY [CHARTER].* HAVING Tally = 1
This assume that all flights have a pilot, but not all flights have a co-pilot. To get the exact display you want, you might have to use it as a sub-query (to remove the Tally column).
SELECT *
FROM charter ch
JOIN crew cr ON ch.char_trip = cr.char_trip
WHERE NOT EXISTS(SELECT *
FROM crew cr2
WHERE cr2.char_trip = ch.char_trip
AND cr2.crew_job != 'PILOT')
I think that should do the trick. The join to the crew table in line 3 is optional, and only if you need results from that table. The NOT EXISTS anti-join is what evaluates all crew for a given trip and checks for any that are not pilots.
You should really help us out here with the schema for us to provide you with a decent query. I think the most important thing here is how do you determine who is a pilot and/or copilot and how do you relate each person to the flight.
I think something like this might help:
SELECT * FROM Charter C
INNER JOIN Crew ON (Charter.CHAR_TRIP = Crew.CHAR_TRIP)
WHERE Crew.Crew_Job = 'PILOT' AND (SELECT COUNT(*) FROM Charter
INNER JOIN Crew ON (Charter.CHAR_TRIP = Crew.CHAR_TRIP)
WHERE Crew.Crew_Job = 'CoPilot'
AND Charter.Chart_Trip = C.ChartTrip) = 0
Although this might not be the cleanest solution.. it should do the work.