Display only result > 2 - sql

I face a problem with the result on my script.
My formula for MARGIN is ((plnamt-(ibhexc/ibhand))/plnamt)*100.
I want to display only result > 2. How to do this? Please help.
This my script:
select
a.plnitm,a.plnstr,max(a.plncdt),max(a.plnndt),max(a.plnamt),max(a.plnevt),b.idept,c.ibhand,c.ibhexc,
decimal((c.ibhexc/c.ibhand),12,4) as AVG_COST,
decimal(((((max(a.plnamt)-(c.ibhexc/c.ibhand))/(max(a.plnamt))))*100),12,4) as MARGIN
from prcpln a
inner join invmst b on a.plnitm = b.inumbr
inner join invbal c on a.plnitm = c.inumbr and a.plnstr = c.istore and c.ibhand <> 0
where a.plnstr = ''14006''
group by a.plnitm,a.plnstr,b.idept,c.ibhand,c.ibhexc
order by a.plnitm

Simplistically, for situations like this, you can take your query and put it inside a cte:
WITH q AS (
select
a.plnitm,a.plnstr,max(a.plncdt),max(a.plnndt),max(a.plnamt),max(a.plnevt),b.idept,c.ibhand,c.ibhexc,
decimal((c.ibhexc/c.ibhand),12,4) as AVG_COST,
decimal(((((max(a.plnamt)-(c.ibhexc/c.ibhand))/(max(a.plnamt))))*100),12,4) as MARGIN
from prcpln a
inner join invmst b on a.plnitm = b.inumbr
inner join invbal c on a.plnitm = c.inumbr and a.plnstr = c.istore and c.ibhand <> 0
where a.plnstr = ''14006''
group by a.plnitm,a.plnstr,b.idept,c.ibhand,c.ibhexc
)
SELECT * FROM q WHERE margin > 2
ORDER BY q.plnitm
This is similar to the advice to use HAVING - a HAVING is a "where clause that is done after a GROUP BY"
A cte is a way of taking some calculated block of data and giving it an alias that can be used just like a table. I wanted to answer this way to point out to you that queries don't have to be formed purely from tables; tables are just blocks of data (with a name), and the output from a select is also "just a block of data" that can be given a name (by use of a cte or subquery) and then used just like a table is

Related

Optimize many subqueries with same source

Does anyone have any idea how to optimize such a query?
How do you encapsulate this into one query?
,(SELECT top 1 WFD_AttDecimal2 as Nocleg
FROM WFElements INNER JOIN
WFElementDetails ON WFElements.WFD_ID = WFElementDetails.DET_WFDID
Where
WFD_STPID = #KrokSlownikUdostepnionyKraj
and dbo.ClearWFElemID (WFD_AttChoose20) = KrajDoDiety6 -- id kraju
and dbo.ClearWFElemID (WFD_AttChoose9) = #Spolka) as NoclegStawka7
,(SELECT top 1 WFD_AttDecimal3 as NoclegPrzelicznik
FROM WFElements INNER JOIN
WFElementDetails ON WFElements.WFD_ID = WFElementDetails.DET_WFDID
Where
WFD_STPID = #KrokSlownikUdostepnionyKraj
and dbo.ClearWFElemID (WFD_AttChoose20) = KrajDoDiety6 -- id kraju
and dbo.ClearWFElemID (WFD_AttChoose9) = #Spolka) as NoclegPrzelicznik7
,PelneDoby6 * (select MinDieta from #MinDieta where RodzajDelegacji = KrajZagr6) as MinIloscDiet7
from #Dieta4
You can use OUTER APPLY or CROSS APPLY and select all the necessary columns within that, you can then access all of them in the SELECT
SELECT
wfd.Nocleg as NoclegStawka7
, wfd.NoclegPrzelicznik as NoclegPrzelicznik7
, d.PelneDoby6 * (select md.MinDieta from #MinDieta md where RodzajDelegacji = KrajZagr6) as MinIloscDiet7
from #Dieta4 d
OUTER APPLY (
SELECT TOP (1)
WFD_AttDecimal2 as Nocleg,
WFD_AttDecimal3 as NoclegPrzelicznik
FROM WFElements INNER JOIN
WFElementDetails ON WFElements.WFD_ID = WFElementDetails.DET_WFDID
Where
WFD_STPID = #KrokSlownikUdostepnionyKraj
and dbo.ClearWFElemID (WFD_AttChoose20) = KrajDoDiety6 -- id kraju
and dbo.ClearWFElemID (WFD_AttChoose9) = #Spolka
) wfd;
Note also:
I suggest you rethink your usage of a scalar function ClearWFElemID as it can be slow. Use a join or a Table Valued function instead.
TOP (1) without an ORDER BY is a code-smell: you may get a different result each time.
Always specify the table alias when using subqueries, or you risk getting the wrong results.

SQL Server Loop Over Records

I have a view vwDocumentLinks with data as shown below:
I have another table RiskTypes with data as below.
The link between the table & viewis : Idx25 = Risk Type and LinkDocNo = DocumentType. What I am trying to achieve is to get DocumentType in RiskTypes that's not in the view vwDocumentLinks foreach BaseValue and where Idx25 = RiskType. An example using a single BaseValue will be:
SELECT * FROM RiskTypeDocuments WHERE RiskType = 'BUSINESS LIMITED COMPANY' AND DocumentType NOT IN (SELECT LINKDOCNO FROM DBO.VWLINKS WHERE BaseValue = '00007573-1637-4B8E-9374-730AF58BCFB6')
I tried the below query and it's not working as expected. Any help will be greatly appreciated. I am a newbie in SQL
SELECT dbo.RiskTypeDocuments.DocumentType,
dbo.RiskTypeDocuments.RiskType,
dbo.vwLinks.BaseValue AS Document
FROM dbo.vwLinks LEFT OUTER JOIN
dbo.RiskTypeDocuments ON dbo.vwLinks.LinkDocNo =
dbo.RiskTypeDocuments.DocumentType AND dbo.vwLinks.Idx25 =
dbo.RiskTypeDocuments.RiskType
WHERE dbo.RiskTypeDocuments.DocumentType IS NOT NULL
ORDER BY Document
NB: I can't change the schema. I can only create views from the existing tables.
Sample Data from the two datasets: vwDocumentLinks & RiskTypeDocuments
SELECT a.DocumentType,
a.RiskType,
b.BaseValue AS Document
FROM dbo.RiskTypeDocuments as a
INNER JOIN (Select distinct Idx25, BaseValue from vwDocumentLinks) as b on b.Idx25 = a.RiskType
where a.DocumentType not in (Select LINKDOCNO from vwDocumentLinks as c where c.basevalue = b.BaseValue )
I think what you are looking for is NOT EXISTS. It takes a little to get your head around, but the idea is to have what is called a correlated subquery. The subquery is joined to the main query, hence the term correlated.
SELECT *
FROM RiskTypeDocuments
WHERE NOT EXISTS (
SELECT 1 FROM vwDocumentLinks
WHERE RiskTypeDocuments.RiskType = vwDocumentLinks.idx25
AND RiskTypeDocuments.DocumentType = vwDocumentLinks.LinkDocNo
)
If you want to retrieve the records which don't exist in the view then
Try like this
SELECT d.DocumentType,
d.RiskType,
v.BaseValue
FROM dbo.vwLinks AS v
OUTER JOIN dbo.RiskTypeDocuments d ON v.LinkDocNo = d.DocumentType AND v.vwLinks.Idx25 =d.RiskType
WHERE v.BaseValue IS NULL
ORDER BY DocumentType

where statement execute before inner join

I'm trying to grab the first instance of each result with a sysAddress of less than 4. However my statement currently grabs the min(actionTime) result first before applying the where sysAddress < 4. I'm trying to have the input for the inner join as the where sysAddress < 4 however i cant seem to figure out how to do it.
Should i be nesting it all differently? I didnt want to create an additional layer of table joins. Is this possible? I'm a bit lost at all the answers ive found.
SELECT
tblHistoryObject.info,
tblHistory.actionTime,
tblHistoryUser.userID,
tblHistoryUser.firstName,
tblHistoryUser.surname,
tblHistory.eventID,
tblHistoryObject.objectID,
tblHistorySystem.sysAddress
FROM tblHistoryObject
JOIN tblHistory
ON (tblHistory.historyObjectID = tblHistoryObject.historyObjectID)
JOIN tblHistorySystem
ON (tblHistory.historySystemID = tblHistorySystem.historySystemID)
JOIN tblHistoryUser
ON (tblHistory.historyUserID = tblHistoryUser.historyUserID)
INNER JOIN (SELECT
MIN(actionTime) AS recent_date,
historyObjectID
FROM tblHistory
GROUP BY historyObjectID) AS t2
ON t2.historyObjectID = tblHistoryObject.historyObjectID
AND tblHistory.actionTime = t2.recent_date
WHERE sysAddress < 4
ORDER BY actionTime ASC
WITH
all_action_times AS
(
SELECT
tblHistoryObject.info,
tblHistory.actionTime,
tblHistoryUser.userID,
tblHistoryUser.firstName,
tblHistoryUser.surname,
tblHistory.eventID,
tblHistoryObject.objectID,
tblHistorySystem.sysAddress,
ROW_NUMBER() OVER (PARTITION BY tblHistoryObject.historyObjectID
ORDER BY tblHistory.actionTime
)
AS historyObjectID_SeqByActionTime
FROM
tblHistoryObject
INNER JOIN
tblHistory
ON tblHistory.historyObjectID = tblHistoryObject.historyObjectID
INNER JOIN
tblHistorySystem
ON tblHistory.historySystemID = tblHistorySystem.historySystemID
INNER JOIN
tblHistoryUser
ON tblHistory.historyUserID = tblHistoryUser.historyUserID
WHERE
tblHistorySystem.sysAddress < 4
)
SELECT
*
FROM
all_action_times
WHERE
historyObjectID_SeqByActionTime = 1
ORDER BY
actionTime ASC
This does exactly what your original query did, without trying to filter by action_time.
Then it appends a new column, using ROW_NUMBER() to generate sequences from 1 for each individual tblHistoryObject.historyObjectID. Then it takes only the rows where this sequence value is 1 (the first row per historyObjectID, when sorted in action_time order).

Query from multiple tables with multiple where conditions in the tables

I'm trying to get a count of all speakers who are active regarding that item as well as the total of speakers who correlate to a certain item. The first LEFT JOIN for the total speakers works, but the other for ONLY the active speakers regarding that item doesn't, any help is appreciated. The SQLFiddle is here
http://sqlfiddle.com/#!3/b579d/1
But when I try to add in the portion where you would get the number of active speakers
(LEFT JOIN (SELECT COUNT (tbl_SpeakerCard_Log.SpeakerName)
WHERE tbl_Speaker_Log.Spoken = 0)
ON tbl_AgendaList.AID = tbl_SpeakerCard_Log.AID)
under the previous LEFT JOIN I get an error. I'm 100% sure the query is wrong in some form, but I'm not sure how to approach it.
*NOTE: Spoken/Active are interchangeable, I just use different wording to clarify what I'm looking for.
EDIT: This is the desired output
http://imgur.com/yP1FKxg
You can use conditional aggregation to do this:
SELECT
AgendaList.AID,
AgendaList.Item,
COUNT(SpeakerList.SPID) as SpeakerTotal,
SUM(CASE WHEN SpeakerList.Spoken = 0 THEN 1 ELSE 0 END) as ActiveSpeakers
FROM AgendaList
LEFT JOIN SpeakerLIST ON AgendaList.AID = SpeakerList.AID
GROUP BY AgendaList.AID, AgendaList.Item;
Sample SQL Fiddle
Or you could use count instead of sum (which might be clearer):
COUNT(CASE WHEN Spoken = 0 THEN Spoken END) as ActiveSpeakers
SQL FIDDLE
WITH sTotal AS (
SELECT AgendaList.AID, AgendaList.Item, COUNT( SpeakerList.SPID) as SpeakerTotal
FROM AgendaList
LEFT JOIN SpeakerLIST ON AgendaList.AID = SpeakerList.AID
GROUP BY AgendaList.AID, AgendaList.Item
),
sActive AS (
SELECT AgendaList.AID, AgendaList.Item, COUNT( SpeakerList.SPID) as SpeakerActive
FROM AgendaList
LEFT JOIN SpeakerLIST ON AgendaList.AID = SpeakerList.AID
WHERE SpeakerLIST.Spoken = 0
GROUP BY AgendaList.AID, AgendaList.Item
)
SELECT sTotal.*, sActive.SpeakerActive
FROM sTotal left join
sActive on sTotal.AID = sActive.AID

Confused in join query in SQL

The following works:
SELECT IBAD.TRM_CODE, IBAD.IPABD_CUR_QTY, BM.BOQ_ITEM_NO,
IBAD.BCI_CODE, BCI.BOQ_CODE
FROM IPA_BOQ_ABSTRCT_DTL IBAD,
BOQ_CONFIG_INF BCI,BOQ_MST BM
WHERE BM.BOQ_CODE = BCI.BOQ_CODE
AND BCI.BCI_CODE = IBAD.BCI_CODE
AND BCI.STATUS = 'Y'
AND BM.STATUS = 'Y'
order by boq_item_no;
Results:
But after joining many tables with that query, the result is confusing:
SELECT (SELECT CMN_NAME
FROM CMN_MST
WHERE CMN_CODE= BRI.CMN_RLTY_MTRL) MTRL,
RRI.RRI_RLTY_RATE AS RATE,
I.BOQ_ITEM_NO,
(TRIM(TO_CHAR(IBAD.IPABD_CUR_QTY,
'9999999999999999999999999999990.999'))) AS IPABD_CUR_QTY,
TRIM(TO_CHAR(BRI.BRI_WT_FACTOR,
'9999999999999999999999999999990.999')) AS WT,
TRIM(TO_CHAR((IBAD.IPABD_CUR_QTY*BRI.BRI_WT_FACTOR),
'9999999999999999999999990.999')) AS RLTY_QTY,
(TRIM(TO_CHAR((IBAD.IPABD_CUR_QTY*BRI.BRI_WT_FACTOR*RRI.RRI_RLTY_RATE),
'9999999999999999999999990.99'))) AS TOT_AMT,
I.TRM_CODE AS TRM
FROM
(SELECT * FROM ipa_boq_abstrct_dtl) IBAD
INNER JOIN
(SELECT * FROM BOQ_RLTY_INF) BRI
ON IBAD.BCI_CODE = BRI.BCI_CODE
INNER JOIN
(SELECT * FROM RLTY_RATE_INF) RRI
ON BRI.CMN_RLTY_MTRL = RRI.CMN_RLTY_MTRL
INNER JOIN
( SELECT IBAD.TRM_CODE, IBAD.IPABD_CUR_QTY,
BM.BOQ_ITEM_NO, IBAD.BCI_CODE, BCI.BOQ_CODE
FROM IPA_BOQ_ABSTRCT_DTL IBAD,
BOQ_CONFIG_INF BCI,BOQ_MST BM
WHERE
BM.BOQ_CODE = BCI.BOQ_CODE
AND BCI.BCI_CODE = IBAD.BCI_CODE
and BCI.status = 'Y'
and bm.status = 'Y') I
ON BRI.BCI_CODE = I.BCI_CODE
AND I.TRM_CODE = BRI.TRM_CODE
AND BRI.TRM_CODE =4
group by BRI.CMN_RLTY_MTRL, RRI.RRI_RLTY_RATE, I.BOQ_ITEM_NO,
IBAD.IPABD_CUR_QTY, BRI.BRI_WT_FACTOR, I.TRM_CODE, I.bci_code
order by BRI.CMN_RLTY_MTRL
Results:
TRM should be 11 instead of 4 in the first row.
you getting 4 because you use
AND BRI.TRM_CODE =4
if you remove this criter you can get true result
In your first query, both of the rows you've highlighted have BCI_CODE=1866.
In the second query, you are joining that result set with a number of others (which come from the same tables, which seems odd). In particular, you are joining from the subquery to another table using BCI_CODE, and from there to (SELECT * FROM ipa_boq_abstrct_dtl) IBAD. Since both of the rows from the subquery have the same BCI_CODE, they will join to the same rows in the other tables.
The quantity that you are actually displaying in the second query is from (SELECT * FROM ipa_boq_abstrct_dtl) IBAD, not from the other subquery.
Is the problem simply that you mean to select I.IPABD_CUR_QTY instead of IBAD.IPABD_CUR_QTY?
You might find this clearer if you did not reuse the same aliases for tables at multiple points in the query.