Select #variable not working - sql

I can't seem to wrap my mind around a little situation I have. I am trying to set a variable using an if condition; I have also tried using a case statement as well, but keep receiving error. Below is what I am working with and it's dynamic SQL as well... The variable I am trying to set is #EBP_Allow... Can someone shine some light on this?
What I need it to do for example...
SELECT
#EBP_Allow = IF #Year > 2014 THEN DO a SELECT ELSE DO Something else...
What I have now...
SELECT
#OU_Allow = Optional_Unit_Allowed_Flag,
#BU_Allow = Basic_Unit_Allowed_Flag,
#EU_Allow = Enterprise_Unit_Allowed_Flag,
#WU_Allow = Whole_Farm_Unit_Allowed_Flag,
#EBP_Allow = I NEED TO USE SUB SELECT, IF OR CASE TO SET THIS
FROM dbo.#YEAR_Insurance_Offer
WHERE
(dbo.#YEAR_Insurance_Offer.State_Code = #StateCode) AND
(dbo.#YEAR_Insurance_Offer.County_Code = #CountyCode) AND
(dbo.#YEAR_Insurance_Offer.Crop_Code = ''#CropCode'') AND
(dbo.#YEAR_Insurance_Offer.Insurance_Plan_ID = #PlanId) #TypeCondition #PracticeCondition
Here's an update; it seems that when ran it's still jumping over my condition...
CASE
WHEN #YEAR < 2015
THEN ''N''
WHEN #YEAR > 2014
THEN (
SELECT Enterprise_Unit_By_Practice_Allowed_Flag
FROM dbo.#YEAR_Insurance_Offer
WHERE
(dbo.#YEAR_Insurance_Offer.State_Code = #StateCode) AND
(dbo.#YEAR_Insurance_Offer.County_Code = #CountyCode) AND
(dbo.#YEAR_Insurance_Offer.Crop_Code = ''#CropCode'') AND
(dbo.#YEAR_Insurance_Offer.Insurance_Plan_ID = #PlanId) #TypeCondition #PracticeCondition
)
END
If I replace the WHEN #Year > 2014 with this...
WHEN #YEAR > 2014
THEN ''N''
It work's just fine... For some reason or another when I have the select in there it's telling me that Enterprise_Unit_By_Practice_Allowed_Flag is an invalid column, but it's not?

In order to set #EPB_Allow you can use a CASE statement and you can add multiple WHEN clauses to treat different situations:
SELECT
#OU_Allow = Optional_Unit_Allowed_Flag,
#BU_Allow = Basic_Unit_Allowed_Flag,
#EU_Allow = Enterprise_Unit_Allowed_Flag,
#WU_Allow = Whole_Farm_Unit_Allowed_Flag,
#EBP_Allow = CASE
WHEN #Year > 2014
THEN (SELECT column FROM Table where conditions1...)
WHEN #Year > 2013
THEN (SELECT column FROM Table where conditions2...)
ELSE (SELECT column FROM Table where conditions3...)
END
FROM dbo.#YEAR_Insurance_Offer
WHERE
(dbo.#YEAR_Insurance_Offer.State_Code = #StateCode) AND
(dbo.#YEAR_Insurance_Offer.County_Code = #CountyCode) AND
(dbo.#YEAR_Insurance_Offer.Crop_Code = ''#CropCode'') AND
(dbo.#YEAR_Insurance_Offer.Insurance_Plan_ID = #PlanId)
#TypeCondition #PracticeCondition
Also, make sure that if you have defined #EBP_Allow as NVARCHAR then you have to make sure that the datatype returned by the query which selects what value you want to assign the variable is of the same datatype (otherwise use conversion functions).
And you have to make sure that the query that you specify in the THEN part will return only one result.

OK first it seems you have fallen into the horrible practice of creating new tables with each year. This is a huge database antipattern and if this is the first year you have done this, I suggest an immediate redesign as this just gets harder and harder to deal with as you add more years.
If you can't, well then at least learn from this and never allow anyone to design a table like this in the future. In enterprise level datbase you can partition by date and in smaller ones, you dont need to have differnt tables just a where clause to filter by year and good indexing.
But you seem to be stuck using dynamic SQL to do everything (another reason why this is a horrible design choice). So you might as well learn how to use dynamic SQl correctly.
First read and thorughly understand this link before you try to write code against this database design:
http://www.sommarskog.se/dynamic_sql.html
Here is an example below of how you build and view the generated SQl for a dynamic SQl statement. This will help you corretly build the statement before you try to execute it.
declare #SQl nvarchar (max), #year nchar (4) = '2014'
set #sql = 'SELECT Enterprise_Unit_By_Practice_Allowed_Flag
FROM dbo.' +#YEAR+ '_Insurance_Offer
WHERE dbo.'+ #YEAR + '_Insurance_Offer.State_Code = #StateCode'
Print #sql
Since you are doing this in Stored procs, all of them should be designed with a debug variable that is used to display any SQL built. It doesn't need to run in the debug mode in prodcution, but when you have a problem (and you can't possibly forsee every variable that will ever be part of creating a dynamic sql statement and there is close to a 100% probability that you will encounter wierd bugs that you have to troubleshoot later. So you need to build in troubleshooting ability into every proc you write with dynamic SQl.
Of course to execute you need to learn to use sp_executesql.
But really read the link, share the link with your managers and coworkers and think about better ways to do this.

dbo.#YEAR_Insurance_Offer.State_Code
[databaseowner].[table].[fieldname]
Table name as variable
According to this table names need to be static.
Honestly, I have never seen a variable table name. Further, I can't think of a good reason to do this. So this is either a logic error or a syntax error?

Related

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).

Making an SQL prodedure to add characters to a string

Hi I'm trying to make a procedure in SQL that adds a bunch of zeroes to a string to complete its length to 18 characters for example:
0446793932' ====> '000000000446793932
and this procedure would go inside an update command,
UPDATE Table SET variable = prototype_procedure('0446793932') WHERE .......
I don't know much about SQL or procedures, if anyone could guide me through something that could help me understand, I would appreciate,
You can use REPLICATE function to achieve what you want.
http://msdn.microsoft.com/en-us/library/ms174383.aspx
DECLARE #t NVARCHAR(10) = N'0446793932'
SELECT #t, REPLICATE(N'0', 18 - LEN(#t)) + #t
depending on what language you are using along the SQL, your best bet is to just add the characters to the string before you send the data to database, that way you don't risk screwing up your tables.
I would using something like this in php for example:
$myvar = "123456";
$myvarLength = strlen($myvar);
if($myvarLength < 12){
while{$myvarLength != 12){
$myvar = "0".$myvar;
$myvarlength = strlen($myvar);
}
}
This should get the job done for you

SQL Update on joined tables with calculated fields

First of all, I know there are already questions and answers about it, this thread being the one that is closest to what I need:
SQL Update to the SUM of its joined values
However, I get a syntax error (operator missing) that seems to occur close to the FROM clause. However I can't see it. Does it not like the FROM itself ? I am not used to using FROM in an update statement but it seems like it's valid from the QA I just linked :|
Any idea why there would be a syntax error there ?
I am using Access 2007 SP3.
Edit:
Wow, I forgot to post the query...
UPDATE r
SET
r.tempsmoy_requete_min = tmm.moy_mob_requete
FROM
rapports AS r INNER JOIN
(SELECT
id_fichier,
Round(Sum(temps_requete_min)/3,0) As moy_mob_requete,
Round(Sum(temps_analyse_min)/3,0) As moy_mob_analyse,
Round(Sum(temps_maj_min)/3,0) As moy_mob_maj,
Round(Sum(temps_rap_min)/3,0) As moy_mob_rap,
Round(Sum(temps_ddc_min)/3,0) As moy_mob_ddc
FROM maintenances
WHERE
periode In (10,9,8) And
annee=2011
GROUP BY id_fichier) AS tmm ON rapports.id_rapport = tmm.id_fichier
WHERE
1=0
The WHERE 1=0 part is because I want to test further the subquery before running it.
Edit: This is some simpler query I am trying. I get a different error this time. It now tells me that tempsmoy_requete_min (and probably all other left operands) are not part of an aggregate function... which is the point of my query. Any idea ?
UPDATE
rapports INNER JOIN maintenances ON rapports.id_rapport = maintenances.id_fichier
SET
rapports.tempsmoy_requete_min = Round(Sum(temps_requete_min)/3,0),
rapports.tempsmoy_analyse_min = Round(Sum(temps_analyse_min)/3,0),
rapports.tempsmoy_maj_min = Round(Sum(temps_maj_min)/3,0),
rapports.tempsmoy_rap_min = Round(Sum(temps_rap_min)/3,0),
rapports.tempsmoy_ddc_min = Round(Sum(temps_ddc_min)/3,0)
WHERE
maintenances.periode In (10,9,8) And
maintenances.annee=2011 AND
1=0
I tried adapting your first query sample, and was able to make your error go away. However then I encountered a different error ('Operation must use an updateable query').
It may be possible to overcome that error, too. However, I found it easier to use a domain function instead of a join to retrieve the replacement value.
UPDATE rapports
SET tempsmoy_requete_min = Round(DSum("temps_requete_min",
"maintenances",
"periode In (10,9,8) AND annee=2011 "
& "AND id_fichier='" & id_rapport
& "'")/3, 0);
If this suggestion works for tempsmoy_requete_min with your data, you will have to extend it to the other fields you want to replace. That won't be pretty. You could make it less ugly with a saved query which you then use as the "Domain" parameter for DSum() ... that could allow you to use a simpler "Criteria" parameter.
UPDATE r
should be
UPDATE rapports
You can't reliably use an alias in the update target.

Delphi query parameter usage when all values is also an option

I have a tquery (going thru BDE or BDE emulating component) that has been used to select either a single record or all records.
Traditionally this has been done as such:
select * from clients where (clientid = :clientid or :clientid = -1)
And then they would put a -1 in the field when they wanted the query to return all values. Going through this code though, I have discovered that when they have done this the query does not use proper indexing for the table and only does a natural read.
Is there a best practices method for achieving this? Perhaps a way to tell a parameter to return all values, or must the script be modified to remove the where clause entirely when all values are desired?
Edit: This is Delphi 7, by the way (And going against Firebird 1.5 sorry for leaving that out)
As you use deprecated BDE, that may be one more reason to migrate from BDE to 3d party solutions. AnyDAC (UniDAC, probably others too. Most are commercial libraries) has macros, which allow to dynamically change a SQL command text, depending on the macro values. So, your query may be written:
ADQuery1.SQL.Text := 'select * from clients {IF &clientid} where clientid = &clientid {FI}';
if clientid >= 0 then
// to get a single record
ADQuery1.Macros[0].AsInteger := clientid
else
// to get all records
ADQuery1.Macros[0].Clear;
ADQuery1.Open;
For the queries with "optional" parameters I always use ISNULL (MSSQL, or NVL Oracle), ie.
SELECT * FROM clients WHERE ISNULL(:clientid, clientid) = clientid
Setting the parameter to NULL then selects all records.
You also have to take care of NULL values in the table fields because NULL <> NULL. This you can overcome with a slight modification:
SELECT * FROM clients WHERE COALESCE(:clientid, clientid, -1) = ISNULL(clientid, -1)
I would use this:
SELECT * FROM CLIENTS WHERE clientid = :clientid or :clientid IS NULL
Using two queries is best:
if (clientid <> -1) then
begin
DBComp.SQL.Text := 'select * from clients where (clientid = :clientid)';
DBComp.ParamByName('clientid').Value := clientid;
end else
begin
DBComp.SQL.Text := 'select * from clients';
end;
DBComp.Open;
...
Alternatively:
DBComp.SQL.BeginUpdate;
try
DBComp.SQL.Clear;
DBComp.SQL.Add('select * from clients');
if (clientid <> -1) then
DBComp.SQL.Add('where (clientid = :clientid)');
finally
DBComp.SQL.EndUpdate;
end;
if (clientid <> -1) then
DBComp.ParamByName('clientid').Value := clientid;
DBComp.Open;
...
Remy's answer may be re-formulated as single query.
It may be better, if you gonna prepare it once and then re-open multiple times.
select * from clients where (clientid = :clientid)
and (:clientid is not null)
UNION ALL
select * from clients where (:clientid is null)
This just aggregates two distinct queries (with same results vector) together. And condition just turns one of those off.
Using would be like that:
DBComp.Prepare.
...
DBComp.Close;
DBComp.ParamByName('clientid').Value := clientid;
DBComp.Open;
...
DBComp.Close;
DBComp.ParamByName('clientid').Clear;
DBComp.Open;
However this query would rely on SQL Server optimizer capability to extract query invariant (:clientid is [not] null) and enable/disable query completely. But well, your original query depends upon that too.
Why still use obsolete FB 1.5 ? Won't FB 2.5.2 work better there ?
I think your original query is formulated poorly.
select * from clients where (:clientid = -1) or ((clientid = :clientid) and (:clientid <> -1))
would probably be easier on SQL Server optimizer. Yet i think FB could do better job there. Try to download later FB, and run your query in it, using IDEs like IBExpert or FlameRobin. Re-arranging parenthesis and changing -1 to NULL are obvious ideas to try.
Using BDE is fragile now. It is not very fast, limiting in datatypes and connectivity (no FB/IB Events for example). And would have all sorts of compatibility problems with Vista/Win7 and Win64. If FB/IB is your server of choice, consider switching to some modern component set:
(FLOSS) Universal Interbase by http://uib.sf.net (RIP all Delphi pages of http://Progdigy.com )
(FLOSS) ZeosLib DBO by http://zeos.firmos.at/
(propr) FIB+ by http://DevRace.com
(propr) IB Objects by http://IBobjects.com
(propr) AnyDAC by http://da-soft.com - sold out to Embarcadero, not-avail for D7
(propr) IB-DAC/UniDAC http://DevArt.com
Also it would be good thing to show the table and indices definition and selectivity of those indices.

Dynamic SQL To Dynamic LINQ in VB.NET with MS SQL Server 2008

I dread asking this question, because with what I've read so far I understand im gonna have to cram a lotta new things into my head. In spite of all the similiar questions(and the wide variety of answers) I thought I'd ask as nothing I've read tailors to what I need specifically enough.
I need to represent the following query using LINQ:
DECLARE #PurchasedInventoryItemID Int = 2
DECLARE #PurchasedInventorySectionID Int = 0
DECLARE #PurchasedInventoryItem_PurchasingCategoryID Int = 3
DECLARE #PurchasedInventorySection_PurchasingCategoryID Int = 0
DECLARE #IsActive Bit = 1
DECLARE #PropertyID Int = 2
DECLARE #PropertyValue nvarchar(1000) = 'Granny Smith'
--Property1, Property2, Property3 ...
SELECT O.PurchasedInventoryObjectID,
O.PurchasedInventoryObjectName,
O.PurchasedInventoryConjunctionID,
O.Summary,
O.Count,
O.PropertyCount,
O.IsActive
FROM tblPurchasedInventoryObject As O
INNER JOIN tblPurchasedInventoryConjunction As C ON C.PurchasedInventoryConjunctionID = O.PurchasedInventoryConjunctionID
INNER JOIN tblPurchasedInventoryItem As I ON I.PurchasedInventoryItemID = C.PurchasedInventoryItemID
INNER JOIN tblPurchasedInventorySection As S ON S.PurchasedInventorySectionID = C.PurchasedInventorySectionID
INNER JOIN tblPurchasedInventoryPropertyMap as M ON M.PurchasedInventoryObjectID = O.PurchasedInventoryObjectID
INNER JOIN tblPropertyValue As V ON V.PropertyValueID = M.PropertyValueID
WHERE
I.PurchasedInventoryItemID = #PurchasedInventoryItemID AND
S.PurchasedInventorySectionID = #PurchasedInventorySectionID AND
I.PurchasingCategoryID = #PurchasedInventoryItem_PurchasingCategoryID AND
S.PurchasingCategoryID = #PurchasedInventorySection_PurchasingCategoryID AND
O.IsActive = #IsActive AND
V.PropertyID = #PropertyID AND
V.Value = #PropertyValue
Now, I know that a query in .NET doesnt look like this, this is my test in the SQL Design Studio. Naturally VB.NET variables will be used in place of the SQL local variables.
My problem is this: All of the conditions after "WHERE" are optional. In that a query might be made that uses one, some, all, or none of the conditions. V.PropertyID and V.Value can also appear any number of times.
In VB.NET I can make this query easy enough by simply concatenating strings, and using a loop to append the "V.PropertyID/V.Value" conditions.
I can also make a Stored Procedure in MS SQL, which is easy enough.
However, I want to accomplish this using LINQ.
If anyone could direct me, I would be most appreciative.
I worked around this by string-concatenating my query and using the DataContext.ExecuteQuery function:
Dim res As IEnumerable(Of tblPurchasedInventoryObject) = pidc.ExecuteQuery(Of tblPurchasedInventoryObject)(query).ToList
Works like a charm, I can even change the data and post it back to the database. It still feels "backwards" though, and im open to better methods of doing this.
“ In VB.NET I can make this query easy enough by simply concatenating strings, and using a loop to append the "V.PropertyID/V.Value" conditions ”
You can make the same thing by using in memory query, by building the query statement and then filter “only when it’s required” by using expression trees
Here’s basics of expression trees
Link
and this is good article on the subject:
Link
you can also refer to page 238 section 9.3 in Jon skeet’s book C# in depth http://www.manning.com/skeet/, and also Linq in Action book http://www.manning.com/marguerie/ all have a good explanation of Expression trees.