DRY - how to extract repeated code to...a stored function maybe? - sql

The third line of each of these statements is exactly the same:
statement1
SELECT * FROM `uploads`
WHERE `uid` = :uid
AND `deleted` <> 1 AND `archived` <> 1
statement2
SELECT * FROM `uploads`
WHERE `folder_id` = :folder_id
AND `deleted` <> 1 AND `archived` <> 1
The third line is used in many other statements as well, lets say 30 different statements; in this contrived example it means "we get all files from an uploads table...that have not been deleted or archived". If in the future we need to add a third qualifier to it (e.g. AND uploadsuccess = 1) we would have to edit 30 different sql statements. Not DRY at all. How can we use DRY principles here, via SQL (MySQL in our case, if that is an important factor)?
Thanks for any help.

I recommend using a VIEW:
CREATE OR REPLACE VIEW uploads_current AS
SELECT * FROM `uploads
WHERE `deleted` <> 1 AND `archived` <> 1;
Then you can query it with additional conditions:
SELECT * FROM `uploads_current`
WHERE `uid` = :uid;
You can redefine the view later:
CREATE OR REPLACE VIEW uploads_current AS
SELECT * FROM `uploads
WHERE `deleted` <> 1 AND `archived` <> 1 AND uploadsuccess = 1;
Then all code that queried uploads_current will implicitly include the same condition, with no code changes required.
Read more about VIEWs: https://dev.mysql.com/doc/refman/5.7/en/create-view.html

This is a common problem, we want all data to be stored, but 99% of cases we're filtering out a certain subset of data.
The solution I usually use is to create a view with the logic in, then query against that. For example:
CREATE VIEW `uploads_view` AS
SELECT * FROM `uploads`
WHERE `deleted` <> 1
AND `archived` <> `;
SELECT * FROM `uploads_view` WHERE `uid` = :uid;
SELECT * FROM `uploads_view` WHERE `folder_id` = :folder_id

Related

how do I join two tables sql

I have an issue that I'm hoping you can help me with. I am trying to create charting data for performance of an application that I am working on. The first step for me to perform two select statements with my feature turned off and on.
SELECT onSet.testName,
avg(onSet.elapsed) as avgOn,
0 as avgOff
FROM Results onSet
WHERE onSet.pll = 'On'
GROUP BY onSet.testName
union
SELECT offSet1.testName,
0 as avgOn,
avg(offSet1.elapsed) as avgOff
FROM Results offSet1
WHERE offSet1.pll = 'Off'
GROUP BY offSet1.testName
This gives me data that looks like this:
Add,0,11.4160277777777778
Add,11.413625,0
Delete,0,4.5245277777777778
Delete,4.0039861111111111,0
Evidently union is not the correct feature. Since the data needs to look like:
Add,11.413625,11.4160277777777778
Delete,4.0039861111111111,4.5245277777777778
I've been trying to get inner joins to work but I can't get the syntax to work.
Removing the union and trying to put this statement after the select statements also doesn't work. I evidently have the wrong syntax.
inner join xxx ON onSet.testName=offset1.testName
After getting the data to be like this I want to apply one last select statement that will subtract one column from another and give me the difference. So for me it's just one step at a time.
Thanks in advance.
-KAP
I think you can use a single query with conditional aggregation:
SELECT
testName,
AVG(CASE WHEN pll = 'On' THEN elapsed ELSE 0 END) AS avgOn,
AVG(CASE WHEN pll = 'Off' THEN elapsed ELSE 0 END) AS avgOff
FROM Results
GROUP BY testName
I just saw the filemaker tag and have no idea if this work there, but on MySQL I would try something along
SELECT testName, sum(if(pll = 'On',elapsed,0)) as sumOn,
sum(if(pll = 'On',1,0)) as numOn,
sum(if(pll ='Off',elapsed,0)) as sumOff,
sum(if(pll ='Off',1,0)) as numOff,
sumOn/numOn as avgOn,
sumOff/numOff as avgOff
FROM Results
WHERE pll = 'On' or pll='Off'
GROUP BY testName ;
If it works for you then this should be rather efficient as you do not need to join. If not, thumbs pressed that this triggers another idea.
The difficulty you have with the join you envisioned is that the filtering in the WHERE clause is performed after the join was completed. So, you would still not know what records to use to compute the averages. If the above is not implementable with FileMaker then check if nested queries work. You would then
SELECT testName, on.avg as avgOn, off.avg as avgOff
FROM ( SELECT ... FROM Results ...) as on, () as off
JOIN on.testName=off.testName
If that is also not possible then I would look for temporary tables.
OK guys... thanks for the help again. Here is the final answer. The statement below is FileMaker custom function that takes 4 arguments (platform, runID, model and user count. You can see the sql statement is specified. FileMaker executeSQL() function does not support nested select statements, does not support IF statements embedded in select statements (calc functions do of course) and finally does not support the SQL keyword VALUES. FileMaker does support the SQL keyword CASE which is a little more powerful but is a bit wordy. The select statement is in a variable named sql and result is placed in a variable named result. The ExecuteSQL() function works like a printf statement for param text so you can see the swaps do occur.
Let(
[
sql =
"SELECT testName, (sum( CASE WHEN PLL='On' THEN elapsed ELSE 0 END)) as sumOn,
sum( CASE WHEN PLL='On' THEN 1 ELSE 0 END) as countOn,
sum( CASE WHEN PLL='Off' THEN elapsed ELSE 0 END) as sumOff,
sum( CASE WHEN PLL='Off' THEN 1 ELSE 0 END) as countOff
FROM Results
WHERE Platform = ?
and RunID = ?
and Model = ?
and UserCnt = ?
GROUP BY testName";
result = ExecuteSQL ( sql ; "" ; ""
; platform
; runID
; model
; userCnt )
];
getAverages ( Result ; "" ; 2 )
)
For those interested the custom function looks like this:
getAverages( result, newList, pos )
Let (
[
curValues = Substitute( GetValue( data; pos ); ","; ¶ );
sumOn = GetValue( curValues; 2 ) ;
countOn = GetValue( curValues; 3 );
sumOff = GetValue( curValues; 4 );
countOff = GetValue( curValues; 5 );
avgOn = sumOn / countOn;
avgOff = sumOff / countOff
newItem = ((avgOff - avgOn) / avgOff ) * 100
];
newList & If ( pos > ValueCount( data); newList;
getAverages( data; If ( not IsEmpty( newList); ¶ ) & newItem; pos + 1 ))
)

Syntax error on WITH clause

I am working on a web app and there are some long winded stored procedures and just trying to figure something out, I have extracted this part of the stored proc, but cant get it to work. The guy who did this is creating alias after alias.. and I just want to get a section to work it out. Its complaining about the ending but all the curly brackets seem to match. Thanks in advance..
FInputs is another stored procedure.. the whole thing is referred to as BASE.. the result of this was being put in a temp table where its all referred to as U. I am trying to break it down into separate sections.
;WITH Base AS
(
SELECT
*
FROM F_Inputs(1,1,100021)
),
U AS
(
SELECT
ISNULL(q.CoverPK,r.CoverPK) AS CoverPK,
OneLine,
InputPK,
ISNULL(q.InputName,r.InputName) AS InputName,
InputOrdinal,
InputType,
ParentPK,
InputTriggerFK,
ISNULL(q.InputString,r.InputString) AS InputString,
PageNo,
r.RatePK,
RateName,
Rate,
Threshold,
ISNULL(q.Excess,r.Excess) AS Excess,
RateLabel,
RateTip,
Refer,
DivBy,
RateOrdinal,
RateBW,
ngRequired,
ISNULL(q.RateValue,r.RateValue) AS RateValue,
ngClass,
ngPattern,
UnitType,
TableChildren,
TableFirstColumn,
parentRatePK,
listRatePK,
NewParentBW,
NewChildBW,
ISNULL(q.SumInsured,0) AS SumInsured,
ISNULL(q.NoItems,0) AS NoItems,
DisplayBW,
ReturnBW,
StringBW,
r.lblSumInsured,
lblNumber,
SubRateHeading,
TrigSubHeadings,
ISNULL(q.RateTypeFK,r.RateTypeFK) AS RateTypeFK,
0 AS ListNo,
0 AS ListOrdinal,
InputSelectedPK,
InputVis,
CASE
WHEN ISNULL(NewChildBW,0) = 0
THEN 1
WHEN q.RatePK is NOT null
THEN 1
ELSE RateVis
END AS RateVis,
RateStatus,
DiscountFirstRate,
DiscountSubsequentRate,
CoverCalcFK,
TradeFilter,
ngDisabled,
RateGroup,
SectionNo
FROM BASE R
LEFT JOIN QuoteInputs Q
ON q.RatePK = r.RatePK
AND q.ListNo = 0
AND q.QuoteId = 100021 )
Well, I explained the issue in the comments section already. I'm doing it here again, so future readers find the answer more easily.
A WITH clause is part of a query. It creates a view on-the-fly, e.g.:
with toys as (select * from products where type = 'toys') select * from toys;
Without the query at the end, the statement is invalid (and would not make much sense anyhow; if one wanted a permanent view for later use, one would use CREATE VIEW instead).

Is there a way to return the currently logged on ID from Netezza in a view's SQL, current_sid

I have a SQL statement on Netezza that uses the following SQL to acquire the currently logged on user ID:
SELECT SESSION_USERNAME FROM _V_SESSION_DETAIL WHERE SESSION_ID=current_sid
This works great when I'm executing the SQL in a database client. However, when I implement the above SQL in a view (along with other SQL) the current_sid is replaced with the session ID I happened to have when I created the view. That SQL will then look something like:
SELECT DEFINITION_SCHEMA."_V_SESSION_DETAIL".SESSION_USERNAME FROM DEFINITION_SCHEMA."_V_SESSION_DETAIL" WHERE (DEFINITION_SCHEMA."_V_SESSION_DETAIL".SESSION_ID = 2434740
Is there a way to define a view that will get the currently logged on user's ID, not the ID that was assigned when the view was created?
Seems like Netezza Metadata functions(ex. current_sid) are not supported in with clause and would advice to remove them from with and to include them in the base query .
CREATE
OR REPLACE VIEW ADMIN.VW_PI_HRCHY_EPH AS
WITH CHAR_MASK(CHAR_MASK_CHAR) AS (
SELECT 'xxx'
FROM _V_SESSION_DETAIL LIMIT 1
)
,NUM_MASK(NUM_MASK_NUM) AS (
SELECT - 1
FROM _V_SESSION_DETAIL LIMIT 1
)
,TS_MASK(TS_MASK_TS) AS (
SELECT '1000-01-01 00:00:00'
FROM _V_SESSION_DETAIL LIMIT 1
)
SELECT CASE
WHEN SECURITY_GRP_CNT.COUNT > 0
THEN PI_HRCHY.HRCHY_LINE_ID
ELSE NUM_MASK.NUM_MASK_NUM
END AS HRCHY_LINE_ID
,CASE
WHEN SECURITY_GRP_CNT.COUNT > 0
THEN PI_HRCHY.LOCALE_CD
ELSE CHAR_MASK.CHAR_MASK_CHAR
END AS LOCALE_CD
,CASE
WHEN SECURITY_GRP_CNT.COUNT > 0
THEN PI_HRCHY.MODIFY_TS
ELSE TS_MASK.TS_MASK_TS
END AS MODIFY_TS
FROM (
SELECT COUNT(*) AS count
FROM _V_USERGROUPS
WHERE USERNAME IN (
SELECT SESSION_USERNAME
FROM _V_SESSION_DETAIL
WHERE SESSION_ID = current_sid
)
AND GROUPNAME = 'GROUP_AUTH2READ'
) SECURITY_GRP_CNT
,ADMIN.PI_HRCHY
,CHAR_MASK
,NUM_MASK
,TS_MASK
WHERE (
(PI_HRCHY.HRCHY_TYP_ID = 11)
AND (PI_HRCHY.ACTV_IND = 'Y'::"NCHAR")
);
The solution NzGuy provided solved my problem. As he stated, apparently placing the current_sid contact in the WITH clause of the SQL causes the constant to be evaluated differently than if it were placed outside the WITH. The common table expression defined outside the WITH clause resolved my problem.

SQL DB2 - conditional logic in WHERE clause

I need to pull back records based on a location ID, but I've got two fields that MAY contain the incoming criteria....
something like this
SELECT * FROM tbl
WHERE myVar = locationID
IF LocationID = 0
myVar = location2ID
this is a 'logical' example...
I think I can do
WHERE myVar = CASE WHEN locationID = 0 THEN location2ID ELSE locationID END
although I've read that CASE in a WHERE clause should be avoided...? why? or is this OK?
- either way it FAILS
WHERE CASE WHEN locationID=0 THEN location2ID=myVAr ELSE locationID=myVar END
also FAILS
thx
Sorry for the confusion lads - didn't mean to be "ambiguous" - looks like #2 will do what I want - but for the clarification requested here is the issue...
the table stores TWO locations, let's call then CURRENT_LOC and ORIGINAL_LOC... in most cases these will BOTH have data, but in some cases the 'thing' hasn't moved... so the CURRENT_LOC is '0'. MY issue is I'll be passing in a LOCATION ID, I want the records where CURRENT_LOC matches OR if the the CURRENT_LOC=0 then I ALSO want the ORIGINAL_LOC... where that matches...
does that help the discussion? I hope.
WHERE myVar = COALESCE(NULLIF(locationID, 0), location2ID)
Alternatively,
WHERE (
(locationID <> 0 AND myVar = locationID)
OR
(locationID = 0 AND myVar = location2ID)
)

No Result Returned From SQL Query

I am running the following query, but no rows are returned even though a record exists that should match the query.
SELECT
*
FROM
tblsignup
WHERE
usr_email='amir#gmail.com'
AND
(status=1 or status=2)
You should try by simplifying the query (yeah...even if it's so simple)
try this
Select * from tblsignup
then
Select * from tblsignup where
usr_email = 'amir#gmail.com'
then
Select * from tblsignup where
usr_email='amir#gmail.com' and
status > 0
//I know you won't use > 0 at the end, but we want to eliminate the most cause of error we simplify by > 0 only to be easier to read
Tell us from where you start getting 0 line, this could lead us to the problem, I know I already had a problem like that with a field named "date", because date is already used by MySQL, funny MySQL still let me use that fieldname tho.
Try this:
select * from `tblsignup` where `usr_email`='amir#gmail.com' and (`status`=1 or `status`=2)
I have a feeling "status" might be reserved for something special. It might be worth a shot changing it to `status`.
Try wrapping brackets around the status column name:
SELECT *
FROM tblsignup
WHERE usr_email = 'amir#gmail.com'
AND ([status] = 1
OR [status] = 2);
EDIT
After reading your comment, why not use:
SELECT *
FROM tblsignup
WHERE usr_email = 'amir#gmail.com'
AND [status] > 0;
May it be that your column or table has case sensitive collation and the address is typed different ('Amir...')? As your query is correct SQL. You can find that with:
EXEC sp_help DatabaseName