Double LEFT JOIN - sql

I want to receive property name and units count and specails count. I have this query:
SELECT
`property`.`property_name`,
COUNT(unit_id) AS `units_count`,
COUNT(special_id) AS `specials_count`
FROM `property`
LEFT JOIN `property_unit` ON unit_property_id = property_id
LEFT JOIN `property_special` ON special_property_id = property_id
WHERE (property_id = '1')
GROUP BY `property_id`
ORDER BY `property_name` ASC
But it is not working properly. If I have one of these left joins - it's ok, but if I have two, I get this result:
["property_name"] => string(11) "Rivers Edge"
["units_count"] => string(1) "2"
["specials_count"] => string(1) "2"
Specials count is 2 and units_count is 2, but units count is really '1'. How can I get correct counts for it?
P.S: for those who know Zend Framework:
$select->setIntegrityCheck(FALSE)
->from(
'property',
array(
'property_name',
)
)
->joinLeft(
'property_unit',
'unit_property_id = property_id',
array(
'units_count' => 'COUNT(unit_id)'
)
)
->joinLeft(
'property_special',
'special_property_id = property_id',
array(
'specials_count' => 'COUNT(special_id)'
)
)
->group('property_id')
->order('property_name');

Try this:
SELECT
`property`.`property_name`,
COUNT(distinct unit_id) AS `units_count`,
COUNT(distinct special_id) AS `specials_count`
FROM `property`
LEFT JOIN `property_unit` ON unit_property_id = property_id
LEFT JOIN `property_special` ON special_property_id = property_id
WHERE (property_id = '1')
GROUP BY `property_id`
ORDER BY `property_name` ASC
EDIT:
You shouldn't always use distinct - it happens to be the right option in this case.
select count(fieldname) returns the number of times that fieldname is not null; select count(distinct fieldname) returns the number of distinct values of fieldname.
In the original query, property_unit and property_special aren't joined to each other, only to property - so for a single property that had 5 units and 7 specials, 35 rows would be returned; therefore count(unit_id) and count(special_id) would both return 35. Since there would be 5 distinct values of unit_id and 7 distinct values of special_id (because these fields uniquely identify their records), count(distinct ...) returns the correct values in these circumstances.

Your SQL should be something like this:
SELECT
`property`.`property_name`,
COUNT(property_unit.unit_id) AS `units_count`,
COUNT(property_special.special_id) AS `specials_count`
FROM `property`
LEFT JOIN `property_unit` ON (property_unit.unit_property_id = property.property_id)
LEFT JOIN `property_special` ON (property_special.special_property_id = property.property_id)
WHERE (property.property_id = '1')
GROUP BY `property.property_id`
ORDER BY `property.property_name` ASC

Related

How to Get a Count of Records Using Partitioning in Oracle

I have the following query:
SELECT
F.IID,
F.E_NUM AS M_E_NUM,
MCI.E_NUM AS MCI_E_NUM,
F.C_NUM AS M_C_NUM,
MCI.C_NUM AS MCI_C_NUM,
F.ET_ID AS M_ET_ID,
EDIE.ET_ID AS ED_INDV_ET_ID,
COUNT(*) OVER (PARTITION BY F.IID) IID_COUNT
FROM FT_T F JOIN CEMEI_T MCI ON F.IID = MCI.IID
JOIN EDE_T EDE ON MCI.E_NUM = EDE.E_NUM
JOIN EDIE_T EDIE ON EDIE.IID = F.IID AND EDIE.ET_ID = EDE.ET_ID
WHERE
F.DEL_F = 'N'
AND MCI.EFF_END_DT IS NULL
AND MCI.TOS = 'BVVB'
AND EDE.PTEND_DT IS NULL
AND EDE.DEL_S = 'N'
AND EDE.CUR_IND = 'A'
AND EDIE.TAR_N = 'Y'
AND F.IID IN
(
SELECT DISTINCT IID
FROM FT_T
WHERE GROUP_ID = 'BG'
AND DEL_F = 'N'
AND (IID, E_NUM) NOT IN
(
SELECT IID, E_NUM FROM CEMEI_T
WHERE TOS = 'BVVB' AND EFF_END_DT IS NULL
)
);
I am basically grabbing information from several tables and creating a flat record of them.
Everything works accordingly except now I need to find out whether there are two records in FT_T table with identical IID's and display that count as part of the result set.
I tried to use partitioning but all the rows in the result set return a single count even though there are ones that have 2 records with identical IID's in FT_T.
The reason I initially said that I'm gathering information from several tables is due to the fact that FT_T might not have all the information I need if two records are not available for the same IID, so I have to retrieve them from other tables JOINed in the query. However, I need to know which FT_T.IID's have two records in FT_T (or greater than one).
Perhaps you need to calculate the count before the join and filtering:
SELECT . . .
FROM (SELECT F.*,
COUNT(*) OVER (PARTITION BY F.IID) as IID_CNT
FROM FT_T F
) JOIN
CEMEI_T MCI
ON F.IID = MCI.IID JOIN
EDE_T EDE
ON MCI.E_NUM = EDE.E_NUM JOIN
EDIE_T EDIE
ON EDIE.IID = F.IID AND EDIE.ET_ID = EDE.ET_ID
. . .
this is merely a comment/observation, but formatting is needed
You use of in(...) with select distinct and not in(...,...) seems complex and could be a problem if some values are NULL. I suggest you consider using EXISTS and NOT EXISTS instead. e.g.
AND EXISTS (
SELECT
NULL
FROM FT_T
WHERE F.IID = FT_T.IID
AND FT_T.GROUP_ID = 'BG'
AND FT_T.DEL_F = 'N'
AND NOT EXISTS (
SELECT
NULL
FROM CEMEI_T
WHERE FT_T.IID = CEMEI_T.IID
AND FT_T.E_NUM = CEMEI_T.E_NUM
AND CEMEI_T.TOS = 'BVVB'
AND CEMEI_T.EFF_END_DT IS NULL
)
)

Subquery returned more than 1 value - MS SQL

select Id,Prayaseeid, name,Gender,
(select name from tb_Category where id = Category) AS Category,
ideadescription,Domain,ProjectTerms,ProjectStartDate,Amountsanctioned,
(select Amount from tb_innovatorDisbursement where tbid ='TBINO1111A' and
applyingforcycle='1') AS AmountDisbursed,
projectstatus,projectoutcome
from tb_innovator
where tbid='TBINO1111A 'and applyingforcycle='1'
Use TOP to Limit rows to 1,
The Select queries in parenthesis resulting in more than one record
select Id,Prayaseeid, name,Gender,
(select TOP(1) name from tb_Category where id = Category) AS Category,
ideadescription,Domain,ProjectTerms,ProjectStartDate,Amountsanctioned,
(select TOP(1) Amount
from tb_innovatorDisbursement where tbid ='TBINO1111A' and applyingforcycle='1') AS AmountDisbursed,
projectstatus,projectoutcome from tb_innovator
where tbid='TBINO1111A 'and applyingforcycle='1'
You need to test your sub queries for the where clause to make sure the values you are searching do not have multiple records. I would use (Top 1 ) for any column other than the Id column assuming the Id column must be unique as the name suggests.
Update query
select ti.Id,
ti.Prayaseeid,
ti.name,
ti.Gender,
(select TOP(1) name from tb_Category where id = ti.Category) AS Category,
ti.ideadescription,
ti.Domain,
ti.ProjectTerms,
ti.ProjectStartDate,
ti.Amountsanctioned,
tid.Amount AS AmountDisbursed,
ti.projectstatus,
ti.projectoutcome
from tb_innovator ti
INNER JOIN tb_innovatorDisbursement tid ON tid.Tbid = ti.tbid
AND tid.applyingforcycle='1'
where ti.tbid='TBINO1111A 'and ti.applyingforcycle='1'
Perhaps you should use JOIN:
select i.Id, i.Prayaseeid, i.name, i.Gender, c.Category,
i.ideadescription, i.Domain, i.ProjectTerms, i.ProjectStartDate, i.Amountsanctioned,
id.Amount from tb_innovatorDisbursement
i.projectstatus, i.projectoutcome
from tb_innovator i left join
tb_Category c
on i.category = c.id left join
tb_innovatorDisbursement id
on id.tbid = i.dbid and id.applyingforcycle = '1'
where i.tbid = 'TBINO1111A 'and i.applyingforcycle = '1';
Note that all column names are qualified as well, indicating what table they come from.

Query specific field based on max sequence number

I have a record set where some of the rows are duplicated. In particular, they are duplicated for the last three rows of the record set. Of the entire four rows, the correct result set that I desire would include the first row and the last row. I desire this because for a particular SARAPPD_TERM_CODE_ENTRY, the record needed is the one where the SARAPPD_SEQ_NO value is at its max. So, the first row because for that particular term, the sequence number is maxed at one and the last row because the sequence number is maxed at six. Image and query are below.
select ppd.sarappd_seq_no, ppd.sarappd_term_code_entry, ppd.sarappd_apdc_code,
dap. dap.saradap_term_code_entry,
spri.spriden_id,
t.sgbstdn_astd_code, t.*
from sgbstdn t
left join spriden spri on t.sgbstdn_pidm = spri.spriden_pidm
left join saradap dap on spri.spriden_pidm = dap.saradap_pidm
join sarappd ppd on dap.saradap_pidm = ppd.sarappd_pidm
where t.sgbstdn_astd_code not in ('AS', 'DS', 'WD', 'SU', 'LA')
and t.sgbstdn_stst_code = 'AS'
and spri.spriden_change_ind is null
and spri.spriden_id = '123456789'
and (ppd.sarappd_apdc_code = 25 or ppd.sarappd_apdc_code = 30
or ppd.sarappd_apdc_code =35)
and ppd.sarappd_term_code_entry = dap.saradap_term_code_entry
--where b.sarappd_term_code_entry = ppd.sarappd_term_code_entry)
order by ppd.sarappd_term_code_entry
I believe this is a simple "where this = ( select max() ) type of query but I've been trying some different things and nothing is working. I'm not getting the results I want. So with that said, any help on this would be greatly appreciated. Thanks in advance.
You could use ROW_NUMBER()
;WITH cte
AS
(select
ROW_NUMBER() OVER (PARTITION BY SARAPPD_TERM_CODE_ENTRY ORDER BY SARAPPD_SEQ_NO DESC) AS RN
ppd.sarappd_seq_no,
ppd.sarappd_term_code_entry,
ppd.sarappd_apdc_code,
dap. dap.saradap_term_code_entry,
spri.spriden_id,
t.sgbstdn_astd_code, t.*
from sgbstdn t
left join spriden spri on t.sgbstdn_pidm = spri.spriden_pidm
left join saradap dap on spri.spriden_pidm = dap.saradap_pidm
join sarappd ppd on dap.saradap_pidm = ppd.sarappd_pidm
where t.sgbstdn_astd_code not in ('AS', 'DS', 'WD', 'SU', 'LA')
and t.sgbstdn_stst_code = 'AS'
and spri.spriden_change_ind is null
and spri.spriden_id = '123456789'
and (ppd.sarappd_apdc_code = 25 or ppd.sarappd_apdc_code = 30
or ppd.sarappd_apdc_code =35)
and ppd.sarappd_term_code_entry = dap.saradap_term_code_entry
order by ppd.sarappd_term_code_entry) a
SELECT *
FROM cte WHERE rn = 1

Only return value that matches the ID on table 1

I have tried all possible joins and sub-queries but I cant get the data to only return one value from table 2 that exactly matches the vendor ID. If I dont have the address included in the query, I get one hit for the vendor ID. How can I make it so that when I add the address, I only want the one vendor that I get prior to adding the address.
The vendor from table one must be VEN-CLASS IS NOT NULL.
This was my last attempt using subquery:
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENMAST.VENDOR_CONTCT,
APVENMAST.TAX_ID,
Subquery.ADDR1
FROM (TEST.dbo.APVENMAST APVENMAST
INNER JOIN
(SELECT APVENADDR.ADDR1,
APVENADDR.VENDOR_GROUP,
APVENADDR.VENDOR,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENADDR APVENADDR
INNER JOIN TEST.dbo.APVENMAST APVENMAST
ON (APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP)
AND (APVENADDR.VENDOR = APVENMAST.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)) Subquery
ON (APVENMAST.VENDOR_GROUP = Subquery.VENDOR_GROUP)
AND (APVENMAST.VENDOR = Subquery.VENDOR))
INNER JOIN TEST.dbo.APVENLOC APVENLOC
ON (APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENLOC.VENDOR)
WHERE (APVENMAST.VEN_CLASS IS NOT NULL)
Try this:
SELECT APVENMAST.VENDOR_GROUP
, APVENMAST.VENDOR
, APVENMAST.VENDOR_VNAME
, APVENMAST.VENDOR_CONTCT
, APVENMAST.TAX_ID
, APVENADDR.ADDR1
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN (
select VENDOR_GROUP, VENDOR, ADDR1
, row_number() over (partition by VENDOR_GROUP, VENDOR order by ADDR1) r
from TEST.dbo.APVENADDR
) APVENADDR
ON APVENADDR.VENDOR_GROUP = APVENMAST.VENDOR_GROUP
AND APVENADDR.VENDOR = APVENMAST.VENDOR
AND APVENADDR.r = 1
--do you need this table; you're not using it...
--INNER JOIN TEST.dbo.APVENLOC APVENLOC
--ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
--AND APVENMAST.VENDOR = APVENLOC.VENDOR
WHERE APVENMAST.VEN_CLASS IS NOT NULL
--if the above inner join was to filter results, you can do this instead:
and exists (
select top 1 1
from TEST.dbo.APVENLOC APVENLOC
ON APVENMAST.VENDOR_GROUP = APVENLOC.VENDOR_GROUP
AND APVENMAST.VENDOR = APVENLOC.VENDOR
)
I found another column in the APVENLOC table that I can filter on to get the unique vendor. Turns out if the vendor address is for the main office, the vendor location is set blank.
Easier than I thought it would be!
SELECT DISTINCT APVENMAST.VENDOR_GROUP,
APVENMAST.VENDOR,
APVENMAST.VENDOR_VNAME,
APVENADDR.ADDR1,
APVENMAST.VENDOR_SNAME,
APVENADDR.LOCATION_CODE,
APVENMAST.VEN_CLASS
FROM TEST.dbo.APVENMAST APVENMAST
INNER JOIN TEST.dbo.APVENADDR APVENADDR
ON (APVENMAST.VENDOR_GROUP = APVENADDR.VENDOR_GROUP)
AND (APVENMAST.VENDOR = APVENADDR.VENDOR)
WHERE (APVENADDR.LOCATION_CODE = ' ')
Shaji

Distinct, count, group by query madness

I am trying to return a count of tests taken per term. I can get the count to return, but I can't get it grouped by term.
I've tried everything and the closest I get is grouping by term but then my count only = 1, which isn't right.
Here is what I have now. It just returns a count, how do I group it by term_id?
SELECT COUNT(*)
FROM (SELECT DISTINCT ON(student_id, test_event_id, terf.term_id) student_id
FROM report.test_event_result_fact terf
JOIN report.growth_measurement_window gw on gw.term_id = terf.term_id
JOIN report.term t on t.term_id = terf.term_id
JOIN report.test tt on tt.test_id = terf.test_id
WHERE terf.partner_id = 98
AND growth_event_yn = 't'
AND gw.test_window_complete_yn = 't'
AND gw.growth_window_type = 'DISTRICT'
AND tt.test_type_description = 'SURVEY_WITH_GOALS') as TestEvents
Without knowing more about your setup, that's my best bet:
select term_id, count(*) AS count_per_term
from (
select Distinct on (student_id, test_event_id, terf.term_id)
terf.term_id, student_id
from report.test_event_result_fact terf
join report.growth_measurement_window gw using (term_id)
join report.term t using (term_id)
join report.test tt using (term_id)
where terf.partner_id = 98
and growth_event_yn = 't'
and gw.test_window_complete_yn = 't'
and gw.growth_window_type = 'DISTRICT'
and tt.test_type_description = 'SURVEY_WITH_GOALS') as TestEvents
group by 1;