Extracting null fields in SQL Server - sql

I will try to be as detailed as possible. Our corporate controller has asked me for some information regarding our suppliers. Here are the tables:
spp = supplier table, each supplier has one record, there are 5,222 records
ast = supplier account profile, there is a (M, 1) relationship between this table and spp, there are 8,950 records in this table. Each duplicate spp_id has a different atp_id which is a transaction profile.
crt = bank account information, a supplier may or may not have bank account info
xvd = table of checking tables, xvd.xcd_id is the field that holds the checking table id. Checking table 0007 is the table that contains the discount info.
Here is my script:
select spp.spp_id supp_num,
spp.spp_matchname supp_name,
case when spp.spp_ddcalculation = 0
then 'End of Month'
else
case when spp.spp_ddcalculation = 1
then 'Net'
else
case when spp.spp_ddcalculation = 2
then 'End of 10, 20, 30'
else
case when spp.spp_ddcalculation = 3
then 'End of 15 or 30'
else null
end
end
end
end calculation1,
convert(varchar(2), spp.spp_ddduration) + case when spp.spp_ddmd = 0
then ' Days'
else case when spp.spp_ddmd = 1
then ' Months'
else null
end
end duration1,
spp.spp_ddday stop_day1,
xvd.xvd_desc discount,
crt.crt_name bank,
case when ast.ast_ddcalculation = 0
then 'End of Month'
else
case when ast.ast_ddcalculation = 1
then 'Net'
else
case when ast.ast_ddcalculation = 2
then 'End of 10, 20, 30'
else case when ast.ast_ddcalculation = 3
then 'End of 15 or 30'
else null
end
end
end
end
calculation2,
convert(varchar(2), ast.ast_ddduration) + case when ast.ast_ddmd = 0
then ' Days'
else case when ast.ast_ddmd = 1
then ' Months'
else null
end
end
duration2,
ast.ast_ddday stop_day2
from spp left join ast on spp.spp_id = ast.spp_id
left join crt on ast.crt_id = crt.crt_id
inner join xvd on ast.cfd_id = xvd.xcv_id
where xvd.xcd_id = '0007'
and xvd.lng_id = 0
order by spp.spp_id
The problem is that there are 371 records in the ast table that have a non null cfd_id which is the field that relates to the discount in checking table 0007. When I run this I get 371 records, but I need all suppliers, even those with null discounts. I know the problem is a combination of my joins and the fact that there is not a null xcv_id in checking table 0007. Can anyone see anything glaring that I have overlooked?
To recap, there are 8,950 records in ast, but only 371 of them have a non null cfd_id. I need to grab all 8,950 records, I can't seem to extract the null discounts. I think I can probably pull everything into a temp table then grab the discounts, but am wondering if there is a way to do this in one select statement.
Thanks
Tony
Edit: The last line of my from statement seems to be the primary issue
inner join xvd on ast.cfd_id = xvd.xcv_id
There are no null xcv_id but there are null cfd_id. Is there another way to join those two tables, besides checking for equality?
Forgot to mention, we are on SQL Server 2008 R2.

Does this solve the problem ?
FROM spp
LEFT JOIN ast
ON spp.spp_id = ast.spp_id
LEFT JOIN crt
ON ast.crt_id = crt.crt_id
INNER JOIN xvd
ON xvd.xcv_id = ast.cfd_id
WHERE xvd.xcd_id = '0007'
AND xvd.lng_id = 0

I think you can just change your inner join to a left join:
from spp left join ast on spp.spp_id = ast.spp_id
left join crt on ast.crt_id = crt.crt_id
inner join xvd on ast.cfd_id = xvd.xcv_id
to
from spp left join ast on spp.spp_id = ast.spp_id
left join crt on ast.crt_id = crt.crt_id
left join xvd on ast.cfd_id = xvd.xcv_id
If you are saying that you want to select records where xvd.xcd_id is 0007 or null then change your where clause to this:
(xvd.xcd_id = '0007' OR xvd.xcd_id is null)

This sounds like a perfect use for views. Instead of trying to build one complex query, you could build a series of views that build upon one another filtering the data the way you want it... then apply the final query to the last view.

Related

Executing a CASE statement with multiple conditions

Query takes two tables does a left join, then filters out based off preferences. It runs well, however, when I add the 'case' statement I run into issues. The idea was to grab the first digits I need to classify them into a broader category. I need to be able to change the names of a field into something friendly. Any suggestions?
Values for the tr.TENANT_NAICS field follow a format similar to this:
543330- Other Computer Related | 2782 Science
548972- Socials
Would like to change to just:
Other Computer Related
Other Computer Related
Query
select
tr.OCCUPANCY_DATE, tr.END_DATE, tr.TENANT_NAICS,
pr.PROPERTY_STATUS, pr.NRA_BUILDING, pr.MARKET, pr.SUBMARKET, pr.FULL_ADDRESS_ONE_LINE,
pr.LEGAL_OWNER, pr.TRUE_OWNER,
lr.LESSOR_SUBLESSOR,
/* trying to fix here */
(case
when left(tr.TENANT_NAICS, 2)::numeric = 11 THEN 'Other Computer Related'
when left(tr.TENANT_NAICS, 2)::numeric = 21 THEN 'Mining'
when left(tr.TENANT_NAICS, 4)::numeric = 4821 THEN 'Construction'
else tr.TENANT_NAICS
end) as "Tenant Industry"
from space.tran tr
left join property pr on tr.ID = pr.ID
left join lease lr on tr.OID = lr.OID
where
tr.STATUS = 'Existing' and tr.MARKET = 'Seattle'
;
Edit: the exact error I receive is 'Numeric value 'Bu' is not recognized'.
Apparently TENANT_NAICS is a varchar column and there must be at least one value that starts with BU instead of digits. Try this SQL:
select
tr.OCCUPANCY_DATE, tr.END_DATE, tr.TENANT_NAICS,
pr.PROPERTY_STATUS, pr.NRA_BUILDING, pr.MARKET, pr.SUBMARKET, pr.FULL_ADDRESS_ONE_LINE,
pr.LEGAL_OWNER, pr.TRUE_OWNER,
lr.LESSOR_SUBLESSOR,
/* trying to fix here */
(case
when left(tr.TENANT_NAICS, 2) = '11' THEN 'Other Computer Related'
when left(tr.TENANT_NAICS, 2) = '21' THEN 'Mining'
when left(tr.TENANT_NAICS, 4) = '4821' THEN 'Construction'
else tr.TENANT_NAICS
end) as "Tenant Industry"
from space.tran tr
left join property pr on tr.ID = pr.ID
left join lease lr on tr.OID = lr.OID
where
tr.STATUS = 'Existing' and tr.MARKET = 'Seattle'
;

WHERE Statement - Multiple conditions - adding 2 more

I can't quite get my conditions correct to alter an existing query that works.
SELECT dd.GasYear,D.DealCash_IsAgency,D.DealCash_IsNetBack, dd.GasYearName,dd.MonthName,dd.Season,dd.FirstOfMonthDate,Deal_OrigDate,DATENAME(WEEKDAY, Deal_OrigDate) AS Orig_DayofWeek
,Deal_StartDate,DATENAME(WEEKDAY, Deal_StartDate) AS Start_DayofWeek,Deal_StopDate,DATENAME(WEEKDAY, Deal_StopDate) AS Stop_DayofWeek
,CASE
WHEN CAST(DATEDIFF(d, Deal_StartDate, Deal_StopDate) AS CHAR(10)) > 6 THEN 'All Other'
END AS DealType
,Deal_Owner,d.Deal_Id,CP_Cmp_ShortName
,CASE
WHEN Qty_MeasurementType_Short = 'GJ' THEN DealCashTran_Qty / 2.055098
ELSE DealCashTran_Qty
END DealCashTran_Dth_Qty
FROM uvDealCash d
LEFT OUTER JOIN uvDealCashTran AS dct
ON d.Deal_Id = dct.Deal_Id
INNER JOIN Cmp c
ON d.CP_Cmp_ShortName = c.Cmp_ShortName
INNER JOIN [uvCmp_CmpType_CmpCPType] ct
ON c.Cmp_Id = ct.Cmp_Id
INNER JOIN BISandbox.dbo.dimDate dd
ON d.Deal_StartDate = dd.FullDate
WHERE ((Deal_Owner IN ('TYK', 'JTT', 'MML', 'CGG', 'LIO', 'MAT')
AND ct.CmpCPType_Descript = 'Producer')
--US Entities Kristen's wants added
OR (CP_Cmp_ShortName IN ('SEV', 'CNAT', 'TNMKT', 'CENT')))
AND Deal_LengthInMonths >= 0
AND DATEDIFF(d, Deal_StartDate, Deal_StopDate) > 6
AND Deal_StartDate > '4/1/2018' --=<Parameters.StartDate>
AND DealPurchaseSellType_Short = 'P'
AND d.DealCashType_Short IN ('DFM', 'FM')
In addition to the above I want to exclude results if DealCash_IsAgency = 1 or D.DealCash_IsNetBack = 1
I tried several variations by adding this to the end of the existing WHERE statement.
--exclude AMA and NB
AND (D.DealCash_IsAgency = 1 OR D.DealCash_IsNetBack = 1)
It still returns rows with 1's in both of the columns.
Greatly appreciate any suggestions.
Thanks
Brent
if you want to exclude then you have to add a condition like this by adding not:
AND NOT (D.DealCash_IsAgency = 1 OR D.DealCash_IsNetBack = 1)

Joining a derived table postgres

I have 4 tables:
Competencies: a list of obviously competencies, static and a library
Competency Levels: refers to an associated group of competencies and has a number of competencies I am testing for
call_competency: a list of all 'calls' that have recorded the specified competency
competency_review_status: proving whether each call_competency was reviewed
Now I am trying to write this query to count a total and spit out the competency, id and whether a user has reached the limit. Everything works except for when I add the user. I am not sure what I am doing wrong, once I limit call competency by user in the where clause, I get a small subset that ONLY exists in call_competency returned when I want the entire list of competencies.
The competencies not reached should be false, ones recorded appropriate number true. A FULL list from the competency table.
I added the derived table, not sure if this is right, obviously it doesn't run properly, not sure what I'm doing wrong and I'm wasting time. Any help much appreciated.
SELECT comp.id, comp.shortname, comp.description,
CASE WHEN sum(CASE WHEN crs.grade = 'Pass' THEN 1 ELSE CASE WHEN crs.grade = 'Fail' THEN -1 ELSE 0 END END) >= comp_l.competency_break_level
THEN TRUE ELSE FALSE END
FROM competencies comp
INNER JOIN competency_levels comp_l ON comp_l.competency_group = comp.competency_group
LEFT OUTER JOIN (
SELECT competency_id
FROM call_competency
WHERE call_competency.user_id IN (
SELECT users.id FROM users WHERE email= _studentemail
)
) call_c ON call_c.competency_id = comp.id
LEFT OUTER JOIN competency_review_status crs ON crs.id = call_competency.review_status_id
GROUP BY comp.id, comp.shortname, comp.description, comp_l.competency_break_level
ORDER BY comp.id;
(Shooting from the hip, no installation to test)
It looks like the below should do the trick. You apparently had some of the joins mixed up, with a column from a relation that was not referenced. Also, the CASE statement in the main query could be much cleaner.
SELECT comp.id, comp.shortname, comp.description,
(sum(CASE WHEN crs.grade = 'Pass' THEN 1 WHEN crs.grade = 'Fail' THEN -1 ELSE 0 END) >= comp_l.competency_break_level) AS reached_limit
FROM competencies comp
JOIN competency_levels comp_l USING (competency_group)
LEFT JOIN (
SELECT competency_id, review_status_id
FROM call_competency
JOIN users ON id = user_id
WHERE email = _studentemail
) call_c ON call_c.competency_id = comp.id
LEFT JOIN competency_review_status crs ON crs.id = call_c.review_status_id
GROUP BY comp.id, comp.shortname, comp.description
ORDER BY comp.id;

Case Statement using Dates

I have a data set of clients. Each have a Start date and an End Date. A client can have multiple lines with different Start dates and End dates. I have another data set with Claims info and i want to know if they had claims during the time frame there state and end date.
how can i write this?
SELECT
M.[ID]
,EN.StartDate
,EN.EndDate
,[Has Cliams History] (Column to identify if yes or no)
FROM [Test].[dbo].[tblClients] M
left join [test].[dbo].[tblCliams] EN on EN.ID = M.ID
To illustrate the use of a case statement, the ClaimDate field represents the date of a claim. The query would look something like this.
SELECT
M.[ID]
,EN.StartDate
,EN.EndDate
,CASE
WHEN ClaimDate between EN.StartDate AND EN.EndDate THEN 'yes'
ELSE 'no'
END [Has Cliams History]
FROM [Test].[dbo].[tblClients] M
left join [test].[dbo].[tblClaims] EN on EN.ID = M.ID
Your question needs to provide more clarity on the definition of both tables and the relationship between the two. I am guessing you aren't actually joining a column called ID from both (at least I hope not). Here is a stab at it based on the assumption you have a ClientID on the tblClaims and also that you support ongoing clients via a NULL EndDate. Also assuming there are Start and End dates on tblClaims.
SELECT M.[ID]
, CASE WHEN MAX(claim.ID) IS NOT NULL THEN 1 ELSE 0 END AS HasClaimsHistory
FROM [Test].[dbo].[tblClients] client
LEFT JOIN [Test].[dbo].[tblCliams] claim on client.ID = claim.ClientID
AND claim.StartDate >= client.StartDate
AND (
client.EndDate IS NULL /* ongoing support? */
OR
claim.EndDate <= client.EndDate
)
GROUP BY M.[ID]
, CASE WHEN MAX(claim.ID) IS NOT NULL THEN 1 ELSE 0 END

Adding columns in result in SQL from one column

First time here so I didn't see a question like this so I hope you can help me.
I am attempting to add 2 columns to a query result.
If aa.actionid = '123', return a result in a column called test1.
Ifaa.actionid = '5', then create a column called this to put the price per ton in that column called either test 1 or test 2
This is what I have so far. I am not really sure if this is right:
SELECT CO.INVOICE_PREFIX
,C.CUSTOMER_NAME
,S.SITE_NAME
,W.DESCRIPTION
,AA.PER_TON_PRICE
,AA.ACTION_ID
,AA.APPLICABLE_ACTION_ID
,OI.VALID_UNTIL
,CON.END_DATE
,CC.DESCRIPTION
,(case when aa.actionid = '123' then aa.per_ton_price Test1
,(case when aa.actionid = '5' then) aa.per_ton_price test2
FROM CUSTOMER C
JOIN SITE S ON C.CUSTOMER_ID = S.CUSTOMER_ID
JOIN CONTRACT CON ON C.CUSTOMER_ID = CON.CUSTOMER_ID
JOIN SITE_ORDER SO ON SO.SITE_ID = S.SITE_ID
JOIN COMPANY_OUTLET CO ON SO.COMPANY_OUTLET_ID = CO.COMPANY_OUTLET_ID
JOIN ORDER_ITEM OI ON OI.ORDER_ID = SO.SITE_ORDER_ID
JOIN PRODUCT P ON OI.PRODUCT_ID = P.PRODUCT_ID
JOIN APPLICABLE_ACTION AA ON AA.ORDER_ITEM_ID = OI.ORDER_ITEM_ID
JOIN COMPETITIVE_CONDITION CC ON CC.COMPETITIVE_CONDITION_ID = OI.COMPETITIVE_CONDITION_ID
LEFT JOIN WASTE W ON W.WASTE_ID = AA.WASTE_ID
JOIN ACTION A ON AA.ACTION_ID = A.ACTION_ID
WHERE AA.ACTION_ID IN ('123','5')
AND P.PRODUCT_ID='2'
AND OI.VALID_UNTIL >= GETDATE()
You will always need to return both columns for all rows. What you can do is map the 'unused' column (dependent on the row data) to a default value, such as null, e.g.:
,case when aa.actionid = '123' then aa.per_ton_price else null end Test1
,case when aa.actionid = '5' then aa.per_ton_price else null end test2
The database is only going to return a TABLE, so if you think about the concept of a Table, could one row have different amount of columns than the others? Probably not. So what you really want to be doing is set the particular column's value to null instead of trying to not output that column at all.