How can I calculate a field based on fields in other tables while inserting a record (in a procedure) - plsql

I'm sorry for the confusing title, but couldn't find another way to ask it.
Let's say I want to write a procedure add_salesline. I enter all the fields with parameters, except subtotal. Subtotal (just the price for the salesline) needs to be calculated based on fields in other tables such as productprice in the table products, pricereduction in the table promotion, etc. (based on the properties).
How can I do this? I've been trying to solve this problem for a good week now, and it's just not working...

Presumably one of the parameters passed to procedure add_salesline() is productid, or whatever. So you use that to SELECT products.productprice, promotion.pricereduction and whatever else you need to perform the calculation.
The purpose of writing a stored procedure is to associate several calls into a single program unit. So add_salesline() might look something like this (lots of caveats because your question is very light on details):
create or replace procedure add_salesline(
p_orderno in salesline.orderno%type
, p_salesqty in salesline.salesqty%type
, p_productid in products.productid%type
)
is
new_rec salesline%rowtype;
begin
new_rec.orderno := p_orderno;
new_rec.salesqty := p_salesqty;
new_rec.productid := p_productid;
select p_salesqty * (p.productprice * nvl(pp.pricereduction, 1))
into new_rec.subtotal
from products p
left outer join promotion pp
on pp.productid = p.productid
where p.productid = p_productid
;
insert into salesline
value new_rec;
end;
This code assumes pricereduction is a rate. If the value is an absolute discount the formula will be different (p.productprice - nvl(pp.pricereduction, 0)). Or if it's a replacement price: coalesce(pp.pricereduction, p.productprice).

Related

expression is of wrong type for function

CREATE OR REPLACE FUNCTION k_w_b_salary(k IN NUMBER, b IN BOOLEAN)
RETURN EMP.ENAME%TYPE IS
names name_table;
BEGIN
IF (b = true) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL ASC)
WHERE ROWNUM <= k;
RETURN names;
ELSIF (b = false) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL DESC)
WHERE ROWNUM <= k;
RETURN names;
END IF;
END;
And I get this error:
12/9 PL/SQL: Statement ignored
12/16 PLS-00382: expression is of wrong type
20/9 PL/SQL: Statement ignored
20/16 PLS-00382: expression is of wrong type
I have this function that tries to find the best/worst paid employees. But i get the above error.
I think it's something to do with the ROWNUM but I am not sure.
I think the lines the error points out are not the lines with the error.
I had this function writen differently and the lines in the error where pointing to the ROWNUM <= k lines.
I have tried putting a fixed number there (<= 3) for example and I got the same error.
I have no idea what else to try, i can't really understand why this is not working.
It's not obvious to me why this is not working. I think it should work fine but obviously it dousen't.
The code for the table i use is :
CREATE OR REPLACE TYPE name_table IS TABLE OF VARCHAR2(10);
Any help is appreciated!
In the function declaration, you said
RETURN EMP.ENAME%TYPE
I assume the data type of column ENAME in table EMP is some sort of string (VARCHAR2(40) or similar) - right?
In the declarations section, you declare a variable names of data type name_table. You didn't show us the definition of the name_table type (that must be given outside the function, not in it); we can probably assume it is a nested table of some sort. Right? [EDIT - I take that back; you did show us your definition of name_table, at the end of your question.]
In the end, your function returns names. Which is of type name_table. But you said the function returns something else: EMP.ENAME%TYPE. In particular, you said the function returns a scalar data type, but you are returning a collection.
This will not work even if the collection has a single element. A table with a single "record" is not the same data type as the "record" itself - even if an actual table has a single "record" in it.
(And, much more so, when the table has three records in it!)
Rather: It seems that you want a table function: one that returns a table of things. If so, then declare the function that way. Perhaps you want the function to
RETURN NAME_TABLE
(at the top, in the function declaration)

MS Acess: max(Date/Time field) on query when field may contain 00:00:00

I am trying to build a Query in MS Access that returns the last date/time for a given entity ID. Research shows that using the MAX() function on the corresponding field and using GROUP BY on the remaining fields appears to be the way to go.
However, this doesn't seem to work in the presence of values that hold 0 hours, 0 minutes and 0 seconds, as it shows those values as well. The query's SQL is as follows:
SELECT Int(Historico_Classificacoes.ID_Entidade) AS ID_Entidade, Max(Historico_Classificacoes.Timestamp_Classificacao) AS [Data da última classificação], Historico_Classificacoes.US_Indicia_Pais_Constituicao, Historico_Classificacoes.US_Indicia_Responsabilidades_Fiscais, Historico_Classificacoes.US_Indicia_Morada_Coletiva, Historico_Classificacoes.US_Indicia_Telefone, Historico_Classificacoes.US_Indicia_Proveniencia_Capital, Historico_Classificacoes.US_Indicia_Beneficiários, Historico_Classificacoes.US_Indicia_Naturalidade, Historico_Classificacoes.US_Indicia_Nacionalidade, Historico_Classificacoes.US_Indicia_Morada_Singular, Historico_Classificacoes.US_Indicia_Laboral
FROM Historico_Classificacoes
GROUP BY Int(Historico_Classificacoes.ID_Entidade), Historico_Classificacoes.US_Indicia_Pais_Constituicao, Historico_Classificacoes.US_Indicia_Responsabilidades_Fiscais, Historico_Classificacoes.US_Indicia_Morada_Coletiva, Historico_Classificacoes.US_Indicia_Telefone, Historico_Classificacoes.US_Indicia_Proveniencia_Capital, Historico_Classificacoes.US_Indicia_Beneficiários, Historico_Classificacoes.US_Indicia_Naturalidade, Historico_Classificacoes.US_Indicia_Nacionalidade, Historico_Classificacoes.US_Indicia_Morada_Singular, Historico_Classificacoes.US_Indicia_Laboral
ORDER BY Int(Historico_Classificacoes.ID_Entidade);
The Historico_Classificacoes table currently holds the following data:
"ID_Entidade";"Timestamp_Classificacao";"Classificacao_DMIF";"Notacao_Risco_BCFT";"US_Indicia_Pais_Constituicao";"US_Indicia_Responsabilidades_Fiscais";"US_Indicia_Morada_Coletiva";"US_Indicia_Telefone";"US_Indicia_Proveniencia_Capital";"US_Indicia_Beneficiários";"US_Indicia_Naturalidade";"US_Indicia_Nacionalidade";"US_Indicia_Morada_Singular";"US_Indicia_Laboral"
"62";20/9/2015 00:00:00;1;30;1;1;1;1;1;1;1;1;1;0
"62";28/9/2015 10:43:38;1;30;1;1;1;1;1;1;1;1;1;1
"62";29/9/2015 17:52:24;1;30;1;1;1;1;1;1;1;1;1;1
"62";29/9/2015 17:52:40;1;30;1;1;1;1;1;1;1;1;1;1
"98";20/9/2015 00:00:00;2;15;1;1;1;1;1;1;0;0;0;0
"98";20/9/2015 00:00:01;0;0;0;0;0;0;0;0;0;0;0;0
The query, when executed in Datasheet View, outputs the following:
"ID_Entidade";"Data da última classificação";"US_Indicia_Pais_Constituicao";"US_Indicia_Responsabilidades_Fiscais";"US_Indicia_Morada_Coletiva";"US_Indicia_Telefone";"US_Indicia_Proveniencia_Capital";"US_Indicia_Beneficiários";"US_Indicia_Naturalidade";"US_Indicia_Nacionalidade";"US_Indicia_Morada_Singular";"US_Indicia_Laboral"
62;29/9/2015 17:52:40;1;1;1;1;1;1;1;1;1;1
62;20/9/2015 00:00:00;1;1;1;1;1;1;1;1;1;0
98;20/9/2015 00:00:00;1;1;1;1;1;1;0;0;0;0
98;20/9/2015 00:00:01;0;0;0;0;0;0;0;0;0;0
There are duplicated records for entities 62 and 98, when only one record for each was expected. Am I missing something here? Why are the entries whose values hold 00:00:00 present?
You may want to consider using an additional query as an intermediate step that identifies the MAX Date/Time combination for each group ID first, then a follow up query that pulls the entire record where that Group ID, Date and Time match, this will ensure you won't have to use First or Min on the rest of your fields, and you will always get the correct data
You use Group By for the last fields like US_Indicia_Morada_Singular and US_Indicia_Laboral. You'll have to use First, Last, Min, or Max on these as well.
Here is your attempt (without the repeated alias)
SELECT INT(ID_Entidade) AS ID_Entidade
, MAX(Timestamp_Classificacao) AS [Data da última classificação]
, US_Indicia_Pais_Constituicao
, US_Indicia_Responsabilidades_Fiscais
, US_Indicia_Morada_Coletiva
, US_Indicia_Telefone
, US_Indicia_Proveniencia_Capital
, US_Indicia_Beneficiários
, US_Indicia_Naturalidade
, US_Indicia_Nacionalidade
, US_Indicia_Morada_Singular
, US_Indicia_Laboral
FROM Historico_Classificacoes
GROUP BY INT(ID_Entidade)
, US_Indicia_Pais_Constituicao
, US_Indicia_Responsabilidades_Fiscais
, US_Indicia_Morada_Coletiva
, US_Indicia_Telefone
, US_Indicia_Proveniencia_Capital
, US_Indicia_Beneficiários
, US_Indicia_Naturalidade
, US_Indicia_Nacionalidade
, US_Indicia_Morada_Singular
, US_Indicia_Laboral
ORDER BY INT(ID_Entidade);
From you comments, here is SQL that is close to what you need. I have added the field "AnotherField" for you as you may or may not need to add field here.
This currently selects the whole record from the table, but only the single "most recent" record for each value found in the AnotherField is listed.
It may be that you need more that one field where AnotherField appears in the SQL. Think of the field you use instead of AnotherField as being the fields that need to be used to find the maximum date record.
SELECT Main.*
FROM Historico_Classificacoes AS Main
INNER JOIN ( SELECT AnotherField
, MAX(Timestamp_Classificacao) AS [MaxDate]
FROM Historico_Classificacoes
GROUP BY AnotherField
)
AS MostRecent
ON ( Main.AnotherField = MostRecent.AnotherField
AND
Main.Timestamp_Classificacao = MostRecent.MaxDate
)

Stored Procedure value to asp.net page under text box

I have a store procedure which show single row value like
(x.CLTotalAc, x.CLTotalAmt,x.CLAc, x.CLAmt,x.CL,
y.GB_CLTotalAc,y.GB_CLTotalAmt, y.GB_CLAc, y.GB_CLAmt, y.GB_CL).
I want these value to my asp.net C# page under text box (txtCLTotalAmt,
txtCL, txtGB_CLTotalAmt, txtGB_CL)
Store Procedure
ALTER proc [dbo].[Cr_CL_Report]
#ProductName text,
#BranchName text
as
begin
Select x.CLTotalAc,x.CLTotalAmt,x.CLAc, x.CLAmt,x.CL,
y.GB_CLTotalAc,y.GB_CLTotalAmt, y.GB_CLAc, y.GB_CLAmt, y.GB_CL
from
(
SELECT COUNT(dbo.DDBranchName.BCode) AS BrCode,
dbo.TblTotalCL.CLProductName AS ProductName,
SUM(dbo.TblTotalCL.CLTotalAc) AS CLTotalAc,
SUM(dbo.TblTotalCL.CLTotalAmt) AS CLTotalAmt,
SUM(dbo.TblTotalCL.CLAc) AS CLAc, SUM(dbo.TblTotalCL.CLAmt) AS CLAmt,
SUM(dbo.TblTotalCL.CLAmt)/SUM(dbo.TblTotalCL.CLTotalAmt) * 100 AS CL
FROM dbo.DDBranchName INNER JOIN
dbo.TblTotalCL ON dbo.DDBranchName.BCode = dbo.TblTotalCL.CLbrCode
WHERE (dbo.TblTotalCL.CLAsOnDate IN
(SELECT MAX(CLAsOnDate) AS Expr1 FROM dbo.TblTotalCL AS TblTotalCL_1))
AND (dbo.TblTotalCL.CLProductName LIKE #ProductName)
AND (dbo.DDBranchName.BCode LIKE #BranchName)
GROUP BY dbo.TblTotalCL.CLProductName
) x
inner join
(
SELECT COUNT(dbo.DDBranchName.BCode) AS GB_BrCode,
dbo.TblTotalCL.CLProductName AS GB_ProductName,
SUM(dbo.TblTotalCL.CLTotalAc) AS GB_CLTotalAc,
SUM(dbo.TblTotalCL.CLTotalAmt) AS GB_CLTotalAmt,
SUM(dbo.TblTotalCL.CLAc) AS GB_CLAc,
SUM(dbo.TblTotalCL.CLAmt) AS GB_CLAmt,
(SUM(dbo.TblTotalCL.CLAmt) / SUM(dbo.TblTotalCL.CLTotalAmt))*100 AS GB_CL
FROM dbo.DDBranchName INNER JOIN
dbo.TblTotalCL ON dbo.DDBranchName.BCode = dbo.TblTotalCL.CLbrCode
WHERE (dbo.TblTotalCL.CLAsOnDate IN
(SELECT MAX(CLAsOnDate) AS Expr1 FROM dbo.TblTotalCL AS TblTotalCL_1))
AND (dbo.TblTotalCL.CLProductName LIKE #ProductName)
GROUP BY dbo.TblTotalCL.CLProductName
) y on
x.ProductName = y.GB_ProductName
end
Blockquote
how can I solve this?
Getting return value from stored procedure in C#
In the link you can see how to get the values form a stored procedure. Then assign the value to the text box
Start by reading up on Ado.net. Use for instance the example here,under adonet data provider examples/sqlclient. When you have this working you have retrieved the data from the database. (imho avoid table adapters, entity framework and linqtosql if you want to grok it. instead go with manually writing for SqlCommand. or dapper to ease some of the burden.)
Now you have to get it to the web page. First decide on Webforms or Aspnetmvc. Then googlewithbing what the syntax is like.
Wash.
Rinse.
Repeat.
HTH

SQL Select Query Asp.Net

I have a product page on a webpage that shows categories of products. This is done with a listview populated from a database. The issue that I have is that the main supplier has demanded that their products are first in the category list. So what I need to do is run a query that will return the results, display those two categories first and then display the rest alphabetically.
So I've been trying to do this using a UNION ALL query like this:
SELECT cat, cat_id, image FROM prod_categories WHERE cat_id = 19 OR cat_id = 65
UNION ALL
SELECT cat, cat_id, image FROM prod_categories WHERE cat_id <> 19 AND cat_id <> 65
I thought with a union like this it would display the results of the first select query first, but it's not doing that.
I can add an 'order by cat' clause on the end, but obviously that only displays them in the correct order if the two categories I want to display come first alphabetically, which they don't.
If anyone has any ideas how to do this it would be greatly appreciated.
Thanks
How about this:
SELECT cat, cat_id, image FROM prod_categories
order by case when cat_id in (19, 65) then 1 else 2 end, cat_id
Cuts out the need to UNION altogether. Might even produce a more efficient execution plan (possibly...).
(using Transact-SQL for SQL Server - the exact syntax may have to be tinkered for MySql etc)
Try something like this.
SELECT cat, cat_id, image, 1 as [srt]
FROM prod_categories WHERE cat_id = 19 OR cat_id = 65
UNION ALL
SELECT cat, cat_id, image, 2 as [srt]
FROM prod_categories WHERE cat_id <> 19 AND cat_id <> 65
ORDER BY srt ASC, cat_id
Don't hard-code this into your query. What happens when the next supplier wants to come second? Or last? For that matter, you may want to list categories in some sort of "group", anyways.
Instead, you should be using an ordering table (or multiple). Something simple to get you started:
CREATE TABLE Category_Order (categoryId INTEGER -- fk to category.id, unique
priority INTEGER) -- when to display category
Then you want to insert the values for the current "special" categories:
INSERT INTO Category_Order (categoryId, priority) VALUES (19, 2147483647), (65, 0)
You'll also need an entry for rows that are not currently prioritized:
INSERT INTO Category_Order (categoryId, priority)
SELECT catId, -2147483648
FROM prod_categories
WHERE catID NOT IN (19, 65)
Which can then be queried like this:
SELECT cat, cat_id, image
FROM prod_categories
JOIN Category_Order
ON category_id = cat_id
ORDER BY priority DESC, cat
If you write a small maintenance program for this table, you can then push re-ordering duties off onto the correct business department. Reordering of entries can be accomplished by splitting the difference between existing entries, although you'll want a procedure to re-distribute if things get too crowded.
Note that, in the event your db supports a clause like ORDER BY priority NULLS LAST, the entries for non-prioritized categories are unnecessary, and you can simply LEFT JOIN to the ordering table.

MySQL Changing Order Depending On Contents of a Column

I have a MySQL table Page with 2 columns: PageID and OrderByMethod.
I also then have a Data table with lots of columns including PageID (the Page the data is on), DataName, and DataDate.
I want OrderByMethod to have one of three entries: Most Recent Data First, Most Recent Data Last, and Alphabetically.
Is there a way for me to tack an "ORDER BY" clause to the end of this query that will vary its ordering method based on the contents of the "OrderByMethod" column? For example, in this query, I would want to have the ORDER BY clause contain whatever ordering rule is stored in Page 1's OrderByMethod column.
GET * FROM `Data` WHERE `Data`.`PageID`=1 ORDER BY xxxxxx;
Maybe a SELECT clause in the ORDER BY clause? I'm not sure how that would work though.
Thanks!
select Data.*
from Data
inner join Page on (Data.PageID=Page.PageID)
where Data.PageID=1
order by
if(Page.OrderByMethod='Most Recent Data First', now()-DataDate,
if(Page.OrderByMethod='Most Recent Data Last', DataDate-now(), DataName)
);
You can probably do this with the IF syntax to generate a column that you can then order by.
SELECT *, IF(Page.OrderBy = 'Alphabetically', Data.DataName, IF(Page.OrderBy = 'Most Recent Data First', NOW() - Data.DataDate, Data.DataDate - NOW())) AS OrderColumn
FROM Data
INNER JOIN Page ON Data.PageID = Page.PageID
WHERE Page.PageID = 1
ORDER BY OrderColumn
The direction of the ordering is determined in the calculation of the data instead of specifying a direction in the ORDER BY
Can you just append the order by clause to the select statement and rebind the table on postback?
If you want to use the content of the column in Page table as an expression in ORDER BY you have to do it using prepared statements. Let say, you store in OrderByMethod something like "field1 DESC, field2 ASC" and you want this string to be used as it is:
SET #order_by =(SELECT OrderByMethod FROM Page WHERE id = [value]);
SET #qr = CONCAT(your original query,' ORDER BY ', #order_by);
PREPARE stmt FROM #qr;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
If you want the result set to be sorted based on the value of OrderByMethod , you can use IF as it was already mentioned by others, or CASE :
...
ORDER BY
CASE OrderByMethod
WHEN 'val1' THEN field_name1
WHEN 'val2' THEN field_name2
....etc
END

Resources