stored procedure issue using fields from the #temp table - sql

I have a stored procedure that basically does this:
Create table #temp(
field1 int,
field2 int,
total int,
total2 int,
total3 int
)
insert into #temp
(
field1,
field2,
)
Select x.field1, y.field2 from tblA x left join tblB y on x.clientid = y.clientid
This works great, this is very much simplified.
Now what I'm doing is UPDATING values for Total, Total2 and Total3 like so:
update #temp set Total = field1 + field2 <-- works
total2 = field1 + field2 + 300 <-- works
total3 = total2 * 100.0 <-- no value
So here in the update statement above, if I use the field that had already been inserted into the table, they let me UPDATE it, however, since I updated total2 in the table, I am unable to use it in total3? Any idea why?

The totals columns are NULL at the time you run the update.
So total3 is doing this: NULL * 100.0
You can either update total3 separately, once you've populated total2:
update #temp
set total = field1 + field2,
total2 = field1 + field2 + 300
update #temp
set total3 = total2 * 100.0
Or you can replace total2 with the logic that populates it:
update #temp
set total = field1 + field2,
total2 = field1 + field2 + 300,
total3 = (field1 + field2 + 300) * 100.0

This is your update:
update #temp
set Total = field1 + field2 <-- works
total2 = field1 + field2 + 300 <-- works
total3 = total2 * 100.0 <-- no value
You have only set values for field1 and field2, so the other values are empty. You need to understand that the set clause is executed "all-at-once" not sequentially. Column references on the left of the = are the "new" record; column references on the right are the "old" record. Hence, you can solve the problem by repeat the expression.
Another way to solve the problem is to use a subquery, CTE, or apply:
update #temp
set Total = v.new_total,
total2 = v2.new_total2 <-- works
total3 = v2.total2 * 100.0 <-- no value
from #temp t cross apply
(values (field1 + field2) ) v(new_total),
(values (new_total + 300) ) v2(new_total2)

May be try the following:
update #temp set Total = field1 + field2
total2 = field1 + field2 + 300
total3 = (field1 + field2 + 300) * 100.0

The problem you are having is because the total2 variable is being set at the same time you are attempting to access it. In order to make the update work correctly you must update total3 with the same logic used to update total2 OR you have to create a separate update for total3.
This is how I would do it:
update #temp set Total = field1 + field2 <-- works
total2 = field1 + field2 + 300 <-- works
total3 = (field1 + field2 + 300) * 100.0 <-- should get value
Notice that I put the addition logic for total2 in the paranthesis used to calculate total3. This is because the total2 variable holds no value until the statement completes execution.
The alternative is to split the update into 2 separate statments such as this:
update #temp set Total = field1 + field2 <-- works
total2 = field1 + field2 + 300 <-- works
and then
update #temp set total3 = (field1 + field2 + 300) * 100.0

Related

Selecting a specific value within a case statement

I need help with selecting a specific value within a CASE Statement.
For an example
SELECT CASE WHEN Field1 >= (Field2 = 1) THEN Answer
WHEN Field1 < (Field2 = 1) THEN No Answer
ELSE Question END AS Field3
I want to be able to select a specific value in Field2 within the CASE Statement
could be you are looking for
SELECT CASE WHEN Field1 >= Field2 AND Field2 = 1 THEN Answer
WHEN Field1 < Field2 AND Field2 = 1 THEN No Answer
ELSE Question END AS Field3
or more simply
SELECT CASE WHEN Field1 >= 1 THEN Answer
WHEN Field1 < 1 THEN No Answer
ELSE Question END AS Field3
-- 1. You don't specify the fields types.
-- 2. You do a comparison of Field1 with a logical expression (Field2=1). i guess it fails.
-- 3. I will assume Answer, No answer and Question will be the desired string result, and have to be quoted.
-- 4. A Table name is missing. I will assume Field1 and Field2 are fields from MyTable
-- 5. I' am assuming Field1 and Field2 of the same type, and be varchar(10) for example.
DECLARE #Field2 varchar(10) = 1
SELECT
CASE WHEN Field1 >= Field2 THEN 'Answer'
WHEN Field1 < Field2 THEN 'No Answer'
ELSE 'Question'
END AS Field3
FROM MyTable
WHERE Field2 = #Field2

DRY for field formula in Oracle SQL

We have some SQL in Oracle which looks like this:
SELECT
CASE
WHEN
field1 - field2 + field3 < 0
THEN
0
ELSE
field1 - field2 + field3
END
we'd like to specify field1 - field2 + field3 once, whilst not altering the output of the SELECT query. Is there any neat way to do this?
As an alternative, if there is a function which can return a numeric value above 0 and returns 0 for negative values that would work as well.
Use greatest():
select greatest(field1 - field2 + field3, 0)
Something like this. If you wrap it in a stored procedure you can return the result.
DECLARE
n number := field1 - field2 + field3;
BEGIN
CASE
WHEN
n < 0
THEN
0
ELSE
n
END CASE
END;
Try:
SELECT
CASE
WHEN
expr < 0
THEN
0
ELSE
expr
END
FROM (SELECT field1 - field2 + field3 as expr
FROM ... );

SQL - Return 0 When results are empty [duplicate]

I need to know how to return a default row if no rows exist in a table. What would be the best way to do this? I'm only returning a single column from this particular table to get its value.
Edit: This would be SQL Server.
One approach for Oracle:
SELECT val
FROM myTable
UNION ALL
SELECT 'DEFAULT'
FROM dual
WHERE NOT EXISTS (SELECT * FROM myTable)
Or alternatively in Oracle:
SELECT NVL(MIN(val), 'DEFAULT')
FROM myTable
Or alternatively in SqlServer:
SELECT ISNULL(MIN(val), 'DEFAULT')
FROM myTable
These use the fact that MIN() returns NULL when there are no rows.
If your base query is expected to return only one row, then you could use this trick:
select NVL( MIN(rate), 0 ) AS rate
from d_payment_index
where fy = 2007
and payment_year = 2008
and program_id = 18
(Oracle code, not sure if NVL is the right function for SQL Server.)
This would be eliminate the select query from running twice and be better for performance:
Declare #rate int
select
#rate = rate
from
d_payment_index
where
fy = 2007
and payment_year = 2008
and program_id = 18
IF ##rowcount = 0
Set #rate = 0
Select #rate 'rate'
How about this:
SELECT DEF.Rate, ACTUAL.Rate, COALESCE(ACTUAL.Rate, DEF.Rate) AS UseThisRate
FROM
(SELECT 0) DEF (Rate) -- This is your default rate
LEFT JOIN (
select rate
from d_payment_index
--WHERE 1=2 -- Uncomment this line to simulate a missing value
--...HERE IF YOUR ACTUAL WHERE CLAUSE. Removed for testing purposes...
--where fy = 2007
-- and payment_year = 2008
-- and program_id = 18
) ACTUAL (Rate) ON 1=1
Results
Valid Rate Exists
Rate Rate UseThisRate
----------- ----------- -----------
0 1 1
Default Rate Used
Rate Rate UseThisRate
----------- ----------- -----------
0 NULL 0
Test DDL
CREATE TABLE d_payment_index (rate int NOT NULL)
INSERT INTO d_payment_index VALUES (1)
This snippet uses Common Table Expressions to reduce redundant code and to improve readability. It is a variation of John Baughman's answer.
The syntax is for SQL Server.
WITH products AS (
SELECT prod_name,
price
FROM Products_Table
WHERE prod_name LIKE '%foo%'
),
defaults AS (
SELECT '-' AS prod_name,
0 AS price
)
SELECT * FROM products
UNION ALL
SELECT * FROM defaults
WHERE NOT EXISTS ( SELECT * FROM products );
*SQL solution
Suppose you have a review table which has primary key "id".
SELECT * FROM review WHERE id = 1555
UNION ALL
SELECT * FROM review WHERE NOT EXISTS ( SELECT * FROM review where id = 1555 ) AND id = 1
if table doesn't have review with 1555 id then this query will provide a review of id 1.
I figured it out, and it should also work for other systems too. It's a variation of WW's answer.
select rate
from d_payment_index
where fy = 2007
and payment_year = 2008
and program_id = 18
union
select 0 as rate
from d_payment_index
where not exists( select rate
from d_payment_index
where fy = 2007
and payment_year = 2008
and program_id = 18 )
One table scan method using a left join from defaults to actuals:
CREATE TABLE [stackoverflow-285666] (k int, val varchar(255))
INSERT INTO [stackoverflow-285666]
VALUES (1, '1-1')
INSERT INTO [stackoverflow-285666]
VALUES (1, '1-2')
INSERT INTO [stackoverflow-285666]
VALUES (1, '1-3')
INSERT INTO [stackoverflow-285666]
VALUES (2, '2-1')
INSERT INTO [stackoverflow-285666]
VALUES (2, '2-2')
DECLARE #k AS int
SET #k = 0
WHILE #k < 3
BEGIN
SELECT #k AS k
,COALESCE(ActualValue, DefaultValue) AS [Value]
FROM (
SELECT 'DefaultValue' AS DefaultValue
) AS Defaults
LEFT JOIN (
SELECT val AS ActualValue
FROM [stackoverflow-285666]
WHERE k = #k
) AS [Values]
ON 1 = 1
SET #k = #k + 1
END
DROP TABLE [stackoverflow-285666]
Gives output:
k Value
----------- ------------
0 DefaultValue
k Value
----------- ------------
1 1-1
1 1-2
1 1-3
k Value
----------- ------------
2 2-1
2 2-2
Assuming there is a table config with unique index on config_code column:
CONFIG_CODE PARAM1 PARAM2
--------------- -------- --------
default_config def 000
config1 abc 123
config2 def 456
This query returns line for config1 values, because it exists in the table:
SELECT *
FROM (SELECT *
FROM config
WHERE config_code = 'config1'
OR config_code = 'default_config'
ORDER BY CASE config_code WHEN 'default_config' THEN 999 ELSE 1 END)
WHERE rownum = 1;
CONFIG_CODE PARAM1 PARAM2
--------------- -------- --------
config1 abc 123
This one returns default record as config3 doesn't exist in the table:
SELECT *
FROM (SELECT *
FROM config
WHERE config_code = 'config3'
OR config_code = 'default_config'
ORDER BY CASE config_code WHEN 'default_config' THEN 999 ELSE 1 END)
WHERE rownum = 1;
CONFIG_CODE PARAM1 PARAM2
--------------- -------- --------
default_config def 000
In comparison with other solutions this one queries table config only once.
Do you want to return a full row? Does the default row need to have default values or can it be an empty row? Do you want the default row to have the same column structure as the table in question?
Depending on your requirements, you might do something like this:
1) run the query and put results in a temp table (or table variable)
2) check to see if the temp table has results
3) if not, return an empty row by performing a select statement similar to this (in SQL Server):
select '' as columnA, '' as columnB, '' as columnC from #tempTable
Where columnA, columnB and columnC are your actual column names.
Insert your default values into a table variable, then update this tableVar's single row with a match from your actual table. If a row is found, tableVar will be updated; if not, the default value remains. Return the table variable.
---=== The table & its data
CREATE TABLE dbo.Rates (
PkId int,
name varchar(10),
rate decimal(10,2)
)
INSERT INTO dbo.Rates(PkId, name, rate) VALUES (1, 'Schedule 1', 0.1)
INSERT INTO dbo.Rates(PkId, name, rate) VALUES (2, 'Schedule 2', 0.2)
Here's the solution:
---=== The solution
CREATE PROCEDURE dbo.GetRate
#PkId int
AS
BEGIN
DECLARE #tempTable TABLE (
PkId int,
name varchar(10),
rate decimal(10,2)
)
--- [1] Insert default values into #tempTable. PkId=0 is dummy value
INSERT INTO #tempTable(PkId, name, rate) VALUES (0, 'DEFAULT', 0.00)
--- [2] Update the single row in #tempTable with the actual value.
--- This only happens if a match is found
UPDATE #tempTable
SET t.PkId=x.PkId, t.name=x.name, t.rate = x.rate
FROM #tempTable t INNER JOIN dbo.Rates x
ON t.PkId = 0
WHERE x.PkId = #PkId
SELECT * FROM #tempTable
END
Test the code:
EXEC dbo.GetRate #PkId=1 --- returns values for PkId=1
EXEC dbo.GetRate #PkId=12314 --- returns default values
This is what I used for getting a default value if no values are present.
SELECT IF (
(SELECT COUNT(*) FROM tbs.replication_status) > 0,
(SELECT rs.last_replication_end_date FROM tbs.replication_status AS rs
WHERE rs.last_replication_start_date IS NOT NULL
AND rs.last_replication_end_date IS NOT NULL
AND rs.table = '%s' ORDER BY id DESC LIMIT 1),
(SELECT CAST(UNIX_TIMESTAMP (CURRENT_TIMESTAMP(6)) AS UNSIGNED))
) AS ts;

SQL update, string to numeric

In an SQL Server database, I have to update some fields, the query is (, is the decimal separator) :
UPDATE MyTbable
SET
Field1 = CAST('111,11' AS DEC(18,4)),
Field2 = CAST('222,22' AS DEC(18,4)),
Field3 = CAST('333,33' AS DEC(18,4))
WHERE Id = '1'
I receive the error :
Error converting data type varchar to numeric. I tried with the cast, same problem
Field1,Field2 and Field3 are numeric 38,2
Any idea ?
Thanks,
Take the commas out.
UPDATE MyTbable
SET
Field1 = CAST(REPLACE('111,11',',','.') AS DEC(18,4)),
Field2 = CAST(REPLACE('222,22',',','.') AS DEC(18,4)),
Field3 = CAST(REPLACE('333,33',',','.') AS DEC(18,4))
WHERE Id = '1'
Use . instead of ,
UPDATE MyTbable
SET
Field1 = CAST('111.11' AS DEC(18,4)),
Field2 = CAST('222.22' AS DEC(18,4)),
Field3 = CAST('333.33' AS DEC(18,4))
WHERE Id = '1'
If you want to select these values afterwards as string and with comma then do that:
select replace(cast(Field1 as varchar(30)), '.',','),
replace(cast(Field2 as varchar(30)), '.',','),
replace(cast(Field2 as varchar(30)), '.',','),
where Id = '1'
remove the commas in you query like :
UPDATE MyTbable
SET
Field1 = CAST('11111' AS DEC(18,4)),
Field2 = CAST('22222' AS DEC(18,4)),
Field3 = CAST('33333' AS DEC(18,4))
WHERE Id = '1'
or you can rmove the comma using replace:
replace('111,11',',','.')

Summarize or not summarize the result set based on grouping of row

probably sql makes me dizzy when complexity level increases. It is easier to put a for loop and work in c#.
I have a query like
select.field1,.field2, field3,field4
from table1
Suppose this returns rows 1, 2, 3, 4, 5, 6.
I want to return summarized one row if this result has same field2 and field3. if ANY of the rows is different then return all the rows.
Thanks in advance.
Here is Sample data. In this lis row number 1 and row 4 are parent items and others child items.
When Summarizing, row 1 is summarized with all the child items but row number 4 is not summarized with children rows since row number 6 is has a different value field 2.
Field1 Field2 Field3 Field4(parentid)
1 paper cash null
2 Paper cash 1
3 paper cash 1
4 paper cash null
5 paper cash 4
6 pen cash 4
Here I want to return
field1 Field2 Field3 field4(all the child's id)
1 paper cash (2,3)
4 paper cash null
5 paper cash null
6 pen cash null
Hope this is better.
With SQL Server you'll have to create a function to assist with the concatenation and a #temp table to assist with post-processing (to avoid repeated calls to the function across the whole source table). If you move to a database platform released in the last 10 years, you'll have much more efficient solutions at your fingertips. :-)
Setup:
USE tempdb;
GO
CREATE TABLE dbo.[Sample]
(
Field1 INT,
Field2 VARCHAR(32),
Field3 VARCHAR(32),
Field4 INT
);
INSERT dbo.[Sample] SELECT 1,'paper','cash', NULL
UNION ALL SELECT 2,'Paper','cash', 1
UNION ALL SELECT 3,'paper','cash', 1
UNION ALL SELECT 4,'paper','cash', NULL
UNION ALL SELECT 5,'paper','cash', 4
UNION ALL SELECT 6,'pen', 'cash', 4;
GO
Function:
CREATE FUNCTION dbo.ConcatIDs
(
#Field1 INT
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #s VARCHAR(8000);
SELECT #s = COALESCE(#s + ',', '')
+ CONVERT(VARCHAR(12), Field1)
FROM dbo.[Sample] AS s
WHERE Field4 = #Field1
AND NOT EXISTS
(
SELECT 1
FROM dbo.[Sample]
WHERE Field4 = s.Field4
AND Field1 <> s.Field1
AND (Field2 <> s.Field2 OR Field3 <> s.Field3)
);
RETURN #s;
END
GO
The query:
SELECT Field1, Field2, Field3, Field4, f4 = dbo.ConcatIDs(Field1)
INTO #x
FROM dbo.[Sample];
SELECT Field1, Field2, Field3,
[field4(all the child's id)] = '(' + f4 + ')'
FROM #x AS x1
WHERE NOT EXISTS
(
SELECT 1 FROM #x AS x2
WHERE x2.Field1 = x1.Field4
AND x2.f4 IS NOT NULL
);
DROP TABLE #x;
Results:
Field1 Field2 Field3 field4(all the child's id)
------ ------ ------ --------------------------
1 paper cash (2,3)
4 paper cash NULL
5 paper cash NULL
6 pen cash NULL
Cleanup:
DROP TABLE dbo.[Sample];
DROP FUNCTION dbo.ConcatIDs;