I want to compare two columns which come from two different tables.
One of the columns, I need to make SUM for all rows with identity let's say 3 and store to a variable.
After that, compare with one row from other table for same identity 3 and to INSERT something ELSE to BREAK if first_column <= second_column.
Can someone suggest some query for this? For Postgresql...
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_test(p_uid integer, p_project_id integer, p_allocated_time numeric, p_achieved_time numeric, p_task_desc character varying, p_obs character varying, p_date timestamp without time zone)
RETURNS character varying AS
$BODY$
DECLARE sum_alloc_time numeric;
DECLARE alloc_hours integer;
DECLARE fld_id integer;
DECLARE alloc_id integer;
BEGIN
if not "SA_ADM".usp_check_permission(p_uid, 'SA_PRJ', 'usp_add_timesheet_record') then
raise exception 'User ID % dont have permission!', p_uid;
end if;
select a.fld_id into alloc_id from "SD_PRJ".tbl_project_allocation a where a.fld_emp_id = p_uid and a.fld_project_id = p_project_id;
SELECT SUM(fld_allocated_time)
INTO sum_alloc_time
FROM "SD_PRJ".tbl_project_timesheet
WHERE fld_project_id = p_project_id;
SELECT p.fld_allocated_days, p.fld_id
INTO alloc_hours, fld_id
FROM "SD_PRJ".tbl_project p
JOIN "SD_PRJ".tbl_project_timesheet t USING (fld_id)
WHERE t.fld_project_id = p_project_id;
IF #sum_alloc_time <= #alloc_hours THEN
INSERT INTO "SD_PRJ".tbl_project_timesheet
(fld_emp_id, fld_project_id, fld_is_allocated, fld_allocated_time
, fld_achieved_time, fld_task_desc, fld_obs, fld_date)
VALUES (p_uid, p_project_id, coalesce(alloc_id,0), p_allocated_time
, p_achieved_time, p_task_desc, p_obs, p_date);
RAISE NOTICE 'INSERT OK!';
ELSE
RAISE NOTICE 'NOT OK';
END IF;
END
1.tbl_project (fld_id, fld_allocated_days,fld_project_id)
2.tbl_project_timesheet(fld_id,fld_allocated_time,fld_project_id), all INTEGER
I have this , but dosen't work as I wish.Thanks
I think one problem is here:
SELECT p.fld_allocated_days, p.fld_id
INTO alloc_hours, fld_id
FROM "SD_PRJ".tbl_project p
JOIN "SD_PRJ".tbl_project_timesheet t USING (fld_id)
WHERE t.fld_project_id = p_project_id;
That will cough (I think) whenever the select query returns more than one row i.e. whenever tbl_project_timesheet has more than one record for a fld_id,project_id combination.
Anyway. Here's a partial, simplified answer, but hopefully you get the idea...
I wouldn't use local variables. Do the insert in one step:
INSERT INTO timesheet(emp_id,project_id) -- other columns
SELECT
p_uid,p.fld_project_id -- other columns
FROM
projects p
INNER JOIN
(SELECT SUM(fld_allocated_time) as sumtime
FROM timesheet t WHERE fld_project_id = p_project_id) as sumtime_subquery
ON p.fld_allocated_days < sumtime -- just join on the allocated time
WHERE p.fld_project_id = p_project_id;
Now, you need to know if anything was actually inserted. I think you can use the RETURNING option of the INSERT statement, e.g. from here (caveat - I have never used RETURNING, nor set a local variable from a with statement):
WITH ROWS AS (
INSERT INTO timesheet(emp_id,project_id) -- other columns
SELECT
p_uid,p.fld_project_id -- other columns
FROM
projects p
INNER JOIN
(SELECT SUM(fld_allocated_time) as sumtime
FROM timesheet t WHERE fld_project_id = p_project_id) as sumtime_subquery
ON p.fld_allocated_days < sumtime -- just join on the allocated time
WHERE p.fld_project_id = p_project_id
RETURNING 1
)
SELECT COUNT(*) into l_updatedCount FROM rows; -- you have to declare l_updatedCount
-- Now an if statement to handle l_updatedCount
Related
ALTER FUNCTION [dbo].[FSmapping] (
#runID as bigInt,
#TypMap as char(1)
)
RETURNS TABLE
AS
RETURN
IF (#TypMap ='C')
BEGIN
SELECT b.[A/C],g.accountNumber, b.[Process], g.[endingBalanceLC], g.beginningBalanceLC
FROM table1 g
LEFT JOIN table2 b ON b.[Acc2]=g.Account_Synt
where RunID = #runID
END
I am trying to implement a table function in SQL. Basically it should have 3 more IFs and the only thing that's going to switch is the table2, from table2-4 based on inputs C I R S. The error is the IF statement, I know that something is missing, I am having a hard time implementing some sort of a switch function with table as a return value. The Select works flawlessly without the IF syntax, it also says errors on the two variables inside the IF
Must declare the scalar variable runID and TypMap
You need to change your query as multi-statement table-valued functions so your function has to like as below:
CREATE OR ALTER FUNCTION [dbo].[FSmapping] (
#runID as bigInt,
#TypMap as char(1)
)
RETURNS #Table1JoinTable2
TABLE (
--table definition here
Col1 VARCHAR(10)
)
AS
BEGIN
IF (#TypMap ='C')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put select query SELECT b.[A/C],g.accountNumber, b.[Process], g.[endingBalanceLC], g.beginningBalanceLC
--FROM table1 g
--LEFT JOIN table2 b ON b.[Acc2]=g.Account_Synt
--where RunID = #runID
END
IF (#TypMap ='I')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
IF (#TypMap ='I')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
IF (#TypMap ='R')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
IF (#TypMap ='S')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
RETURN
END
If TypMap does not come C, what will the function return?
i think u need 'ELSE'.
IF Boolean_expression
BEGIN
-- Statement block executes when the Boolean expression is TRUE
END
ELSE
BEGIN
-- Statement block executes when the Boolean expression is FALSE
END
I've inherited a query that has parameters which specify pulls data for a single desired month. The extract then gets manually added to previous month's extract in Excel. I'd like to eliminate the manual portion by adjusting the existing query to iterate across all months greater than a given base month, then (if this is what makes most sense) unioning the individual "final" outputs.
My attempt was to add the entire block of code for each specific month to the existing code, and then run it together. The idea was that I'd just paste in a new block each new month. I knew this was very inefficient, but I don't have the luxury of learning how to do it efficiently, so if it worked I'd be happy.
I ran into problems because the existing query has two subqueries which then are used to create a final table, and I couldn't figure out how to retain the final table at the end of the code so that it could be referenced in a union later (fwiw, I was attempting to use a Select Into for that final table).
with eligibility_and_customer_type AS
(SELECT DISTINCT ON(sub_id, mbr_sfx_id)
sub_id AS subscriber_id
, mbr_sfx_id AS member_suffix_id
, src_mbr_key
, ctdv.cstmr_typ_cd
, gdv.grp_name
FROM adw_common.cstmr_typ_dim_vw ctdv
JOIN adw_common.mbr_eligty_by_mo_fact_vw
ON ctdv.cstmr_typ_key = mbr_eligty_by_mo_fact_vw.cstmr_typ_key
AND mbr_eligty_yr = '2018'
AND mbr_eligty_mo = '12'
JOIN adw_common.prod_cat_dim_vw
ON prod_cat_dim_vw.prod_cat_key = mbr_eligty_by_mo_fact_vw.prod_cat_key
AND prod_cat_dim_vw.prod_cat_cd = 'M'
JOIN adw_common.mbr_dim_abr
ON mbr_eligty_by_mo_fact_vw.mbr_key = mbr_dim_abr.mbr_key
JOIN consumer.facets_xref_abr fxf
ON mbr_dim_abr.src_mbr_key = fxf.source_member_key
JOIN adw_common.grp_dim_vw gdv
ON gdv.grp_key=mbr_eligty_by_mo_fact_vw.grp_key),
facets_ip as
(select distinct cl.meme_ck
FROM gpgen_cr_ai.cmc_clcl_claim_abr cl
/* LEFT JOIN gpgen_cr_ai.cmc_clhp_hosp_abr ch
ON cl.clcl_id = ch.clcl_id*/
LEFT JOIN gpgen_cr_ai.cmc_cdml_cl_line cd
ON cl.clcl_id = cd.clcl_id
WHERE cd.pscd_id = '21'
/*AND ch.clcl_id IS NULL*/
AND cl.clcl_cur_sts NOT IN ('91','92')
AND cl.clcl_low_svc_dt >= '20181201'
and cl.clcl_low_svc_dt <= '20181231'
group by 1)
select distinct c.meme_ck,
e.cstmr_typ_cd,
'201812' as Yearmo
from facets_ip c
left join eligibility_and_customer_type e
on c.meme_ck = e.src_mbr_key;
The code above has date parameters that get updated when necessary.
The final output would be a version of the final table created above, but with results corresponding to, say, 201801 - present.
If you provide:
DDL of the underlying tables
Sample Data of the underlying tables
Expected resultset
DBMS you are using
, then one would be able to provide the best solution here.
Without knowing them, and as you said you only care about dynamically looping through each month, here is one way you can utilize your code to loop it through in SQL Server. Please fill the variable #StartDate and #EndDate values and provide proper datatype for meme_ck and cstmr_typ_cd.
IF OBJECT_ID ('tempdb..#TempTable', N'U') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
CREATE TABLE #TempTable
(
meme_ck <ProvideProperDataTypeHere>
,cstmr_typ_cd <ProvideProperDataTypeHere>
,Yearmo VARCHAR(10)
)
DECLARE #StartDate DATE = '<Provide the first day of the start month>'
DECLARE #EndDate DATE = '<Provide the end date inclusive>'
WHILE #StartDate <= #EndDate
BEGIN
DECLARE #MonthEndDate DATE = CASE WHEN DATEADD(DAY, -1, DATEADD(MONTH, 1, #StartDate)) <= #EndDate THEN DATEADD(DAY, -1, DATEADD(MONTH, 1, #StartDate)) ELSE #EndDate END
DECLARE #MonthYear VARCHAR(6) = LEFT(CONVERT(VARCHAR(8), #StartDate, 112), 6)
--This is your code which I am not touching without not knowing any detail about it. Just feeding the variables to make it dynamic
;with eligibility_and_customer_type AS
(SELECT DISTINCT ON(sub_id, mbr_sfx_id)
sub_id AS subscriber_id
, mbr_sfx_id AS member_suffix_id
, src_mbr_key
, ctdv.cstmr_typ_cd
, gdv.grp_name
FROM adw_common.cstmr_typ_dim_vw ctdv
JOIN adw_common.mbr_eligty_by_mo_fact_vw
ON ctdv.cstmr_typ_key = mbr_eligty_by_mo_fact_vw.cstmr_typ_key
AND mbr_eligty_yr = CAST(YEAR(#StartDate) AS VARCHAR(10)) -- NO need to cast if mbr_eligty_yr is an Integer
AND mbr_eligty_mo = CAST(MONTH(#StartDate) AS VARCHAR(10)) -- NO need to cast if mbr_eligty_yr is an Integer
JOIN adw_common.prod_cat_dim_vw
ON prod_cat_dim_vw.prod_cat_key = mbr_eligty_by_mo_fact_vw.prod_cat_key
AND prod_cat_dim_vw.prod_cat_cd = 'M'
JOIN adw_common.mbr_dim_abr
ON mbr_eligty_by_mo_fact_vw.mbr_key = mbr_dim_abr.mbr_key
JOIN consumer.facets_xref_abr fxf
ON mbr_dim_abr.src_mbr_key = fxf.source_member_key
JOIN adw_common.grp_dim_vw gdv
ON gdv.grp_key=mbr_eligty_by_mo_fact_vw.grp_key),
facets_ip as
(select distinct cl.meme_ck
FROM gpgen_cr_ai.cmc_clcl_claim_abr cl
/* LEFT JOIN gpgen_cr_ai.cmc_clhp_hosp_abr ch
ON cl.clcl_id = ch.clcl_id*/
LEFT JOIN gpgen_cr_ai.cmc_cdml_cl_line cd
ON cl.clcl_id = cd.clcl_id
WHERE cd.pscd_id = '21'
/*AND ch.clcl_id IS NULL*/
AND cl.clcl_cur_sts NOT IN ('91','92')
AND cl.clcl_low_svc_dt BETWEEN #StartDate AND #MonthEndDate
group by 1)
INSERT INTO #TempTable
(
meme_ck
,cstmr_typ_cd
,Yearmo
)
select distinct c.meme_ck,
e.cstmr_typ_cd,
#MonthYear as Yearmo
from facets_ip c
left join eligibility_and_customer_type e
on c.meme_ck = e.src_mbr_key;
SET #StartDate = DATEADD(MONTH, 1, #StartDate)
END
SELECT * FROM #TempTable;
I don't have enough information on your tables to really create an optimal solution. The solutions I am providing just have a single parameter (table name) and for your solution, you will need to pass in an additional parameter for the date filter.
The idea of "looping" is not something you'll need to do in Greenplum. That is common for OLTP databases like SQL Server or Oracle that can't handle big data very well and have to process smaller amounts at a time.
For these example solutions, a table is needed with some data in it.
CREATE TABLE public.foo
(id integer,
fname text,
lname text)
DISTRIBUTED BY (id);
insert into foo values (1, 'jon', 'roberts'),
(2, 'sam', 'roberts'),
(3, 'jon', 'smith'),
(4, 'sam', 'smith'),
(5, 'jon', 'roberts'),
(6, 'sam', 'roberts'),
(7, 'jon', 'smith'),
(8, 'sam', 'smith');
Solution 1: Learn how functions work in the database. Here is a quick example of how it would work.
Create a function that does the Create Table As Select (CTAS) where you pass in a parameter.
Note: You can't execute DDL statements in a function directly so you have to use "EXECUTE" instead.
create or replace function fn_test(p_table_name text) returns void as
$$
declare
v_sql text;
begin
v_sql :='drop table if exists ' || p_table_name;
execute v_sql;
v_sql := 'create table ' || p_table_name || ' with (appendonly=true, compresstype=quicklz) as
with t as (select * from foo)
select * from t
distributed by (id)';
execute v_sql;
end;
$$
language plpgsql;
Execute the function with a simple select statement.
select fn_test('foo3');
Notice how I pass in a table name that will be created when you execute the function.
Solution 2: Use psql variables
Create a sql file name "test.sql" with the following contents.
drop table if exists :p_table_name;
create table :p_table_name with (appendonly=true, compresstype=quicklz) as
with t as (select * from foo)
select * from t
distributed by (id);
Next, you execute psql and pass in the variable p_table_name.
psql -f test.sql -v p_table_name=foo4
psql:test.sql:1: NOTICE: table "foo4" does not exist, skipping
DROP TABLE
SELECT 8
CREATE OR REPLACE FUNCTION "freeTicket" (eid integer NOT NULL)
DECLARE
couponCode text
BEGIN
INSERT INTO purchases p (cid, pdate, eid, ccode)
VALUES
(
SELECT p.cid, GETDATE(), $1, couponCode FROM purchase p
GROUP BY p.cid
HAVING COUNT(1) > 5
ORDER BY p.cid
);
END; LANGUAGE plpgsql;
I need to set the variable of couponCode to the output of:
Select code from couponCode where eid = $1 and percentage = 100;
And use it in the insert query above.
What is the best way to do this?
That would be SELECT <expressions> INTO <variables> FROM ..., but you can do it all in one statement:
INSERT INTO purchases p (cid, pdate, eid, ccode)
SELECT p.cid,
current_date,
$1,
(SELECT code FROM couponcode
WHERE eid = $1 AND percentage = 100)
FROM purchase p
GROUP BY p.cid
HAVING COUNT(1) > 5:
ORDER BY makes no sense here.
Basics about assigning variables in PL/pgSQL:
Store query result in a variable using in PL/pgSQL
Apart from that, your function has a number of syntax errors and other problems. Starting with:
CREATE OR REPLACE FUNCTION "freeTicket" (eid integer NOT NULL)
DECLARE ...
NOT NULL isn't valid syntax here.
You must declare the return type somehow. If the function does not return anything, add RETURNS void.
For your own good, avoid CaMeL-case identifiers in Postgres. Use legal, lower-case identifiers exclusively if possible. See:
Are PostgreSQL column names case-sensitive?
The function would work like this:
CREATE OR REPLACE FUNCTION free_ticket(_eid integer, OUT _row_ct int) AS
$func$
DECLARE
coupon_code text; -- semicolon required
BEGIN
INSERT INTO purchases (cid, pdate, eid, ccode)
SELECT cid, now()::date, _eid
, (SELECT code FROM couponCode WHERE eid = _eid AND percentage = 100)
FROM purchase
GROUP BY cid
HAVING COUNT(*) > 5 -- count(*) is faster
ORDER BY cid; -- ORDER BY is *not* pointless.
GET DIAGNOSTICS _row_ct := ROW_COUNT;
END
$func$ LANGUAGE plpgsql;
The added OUT row_ct int is returned at the end of the function automatically. It obviates the need for an explicit RETURNS declaration.
You also had a table alias in:
INSERT INTO purchases p (cid, pdate, eid, ccode)
But INSERT statements require the AS keyword for aliases to avoid ambiguity (unlike other DML statements). So: INSERT INTO purchases AS p .... But no need for an alias since there is no ambiguity in the statement.
Related:
Count the rows affected by plpgsql function
Asides: Two tables named purchase and purchases, that's bound to lead to confusion. And the second table might also be replaced with a VIEW or MATERIALIZED VIEW.
I have been trying to create a View where one of the column pending_amount gets its value as a result of a stored procedure execution.
The stored procedure is pending_stock(int,int) and returns an integer. The view is created successfully but when i try to perform any query like select on the this view it takes ever to return a value.
CREATE OR REPLACE VIEW view_production_parts AS
SELECT p.part_id, p.min_amount, gp.part_num, gp.description
, p.quantity_available
, p.quantity_total - p.quantity_available AS quantity_alloc
, p.quantity_total
, (SELECT pending_stock(p.part_id, 0) AS pending_stock) AS pending_amount
, p.production_run
, CASE
WHEN ppur.purchased_part_id IS NOT NULL THEN true
ELSE false
END AS is_purchased_part, ppur.purchased_part_id, p.store_move_type_id
, gp.part_status_id, p.default_location
, COALESCE(pwoh.part_work_order_hold_id, 0) AS part_work_order_hold_id
FROM general_part gp
JOIN part p ON gp.part_id = p.part_id
LEFT JOIN purchased_part ppur ON ppur.part_id = p.part_id
LEFT JOIN part_work_order_hold pwoh ON pwoh.part_id = p.part_id
ORDER BY gp.part_num;
Can a stored procedure be used in a view? If used, Is my declaration correct?
Find the result from of this query at explain.depesz.com:
EXPLAIN ANALYZE SELECT count(*) FROM view_production_parts
I am using Postgres 8.4.
Function definition for pending_stock(int,int):
CREATE OR REPLACE FUNCTION pending_stock(var_part_id integer
, var_pattern_id integer)
RETURNS integer AS
$BODY$
declare
r record;
var_qty_expected integer;
var_qty_moved_to_stock integer;
var_total_stock_moved_out integer;
var_actual_qty integer;
begin
var_total_stock_moved_out := 0;
var_qty_expected := 0;
for r in
select work_order_id,quantity_expected
from view_work_orders
where part_id = var_part_id and open = 'TRUE'
and quantity_allocated is null and quantity_expected >= quantity_actual
loop
var_qty_expected = var_qty_expected + r.quantity_expected;
select sum(quantity) from view_work_order_move_parts_details
where source_work_order_id = r.work_order_id
and part_id = var_part_id into var_qty_moved_to_stock;
if var_qty_moved_to_stock is null then
var_qty_moved_to_stock = 0;
end if;
var_total_stock_moved_out = var_total_stock_moved_out
+ var_qty_moved_to_stock;
end loop;
var_actual_qty := var_qty_expected - var_total_stock_moved_out;
if var_actual_qty > 0 then
return var_actual_qty;
else
return 0;
end if;
end;
$BODY$
LANGUAGE 'plpgsql' VOLATILE STRICT
COST 100;
ALTER FUNCTION pending_stock(integer, integer) OWNER TO postgres;
View
You don't need a subquery for the function call. And you can simplify some other minor details:
CREATE OR REPLACE VIEW view_production_parts AS
SELECT p.part_id, p.min_amount
, gp.part_num, gp.description, p.quantity_available
, p.quantity_total - p.quantity_available AS quantity_alloc
, p.quantity_total
, pending_stock(gp.part_id, 0) AS pending_amount
, p.production_run
,(ppur.purchased_part_id IS NOT NULL) AS is_purchased_part
, ppur.purchased_part_id, p.store_move_type_id, gp.part_status_id
, p.default_location
, COALESCE(pwoh.part_work_order_hold_id, 0) AS part_work_order_hold_id
FROM general_part gp
JOIN part p USING (part_id)
LEFT JOIN purchased_part ppur USING (part_id)
LEFT JOIN part_work_order_hold pwoh USING (part_id)
ORDER BY gp.part_num;
Other than that the VIEW definition looks fine.
Function
Can be largely simplified:
CREATE OR REPLACE FUNCTION pending_stock(var_part_id integer
, var_pattern_id integer)
RETURNS integer AS
$func$
DECLARE
r record;
var_qty_expected integer := 0;
var_total_stock_moved_out integer := 0;
BEGIN
FOR r IN
SELECT work_order_id, quantity_expected
FROM view_work_orders
WHERE part_id = var_part_id
AND open = 'TRUE' -- A string instead of a boolean?
AND quantity_allocated IS NULL
AND quantity_expected >= quantity_actual
LOOP
var_qty_expected := var_qty_expected + r.quantity_expected;
SELECT var_total_stock_moved_out + COALESCE(sum(quantity), 0)
FROM view_work_order_move_parts_details
WHERE source_work_order_id = r.work_order_id
AND part_id = var_part_id
INTO var_total_stock_moved_out;
END LOOP;
RETURN GREATEST(var_qty_expected - var_total_stock_moved_out, 0);
END
$func$ LANGUAGE plpgsql
Major points
Generally, assignments are comparatively expensive in plpgsql. Every assignment is executed with a (very simple and fast) SELECT statement internally. Try to use fewer of them.
You can init variables at declaration time. No need for another statement.
The assignment operator in plpgsql is :=. = works, but is undocumented.
Use COALESCE() to catch NULL values.
The function parameter var_pattern_id is never used. This is probably not the full function definition.
The whole final part can be replaced with a single statement using GREATEST
Superior query
Now, this cleaned up function will be a bit faster, but not much. Your whole design of looping repeatedly is extremely inefficient. It results in correlated subqueries that loop through correlated subqueries yet again. Performance nightmare.
Recast the problem as set-based operation to make it faster. Well, a lot faster.
SELECT e.part_id
,GREATEST(COALESCE(sum(e.quantity_expected), 0)
- COALESCE(sum(m.total_stock_moved_out), 0), 0)
FROM view_work_orders e
LEFT JOIN (
SELECT source_work_order_id AS work_order_id
,COALESCE(sum(quantity), 0) AS total_stock_moved_out
FROM view_work_order_move_parts_details
WHERE part_id = var_part_id
GROUP BY 1
) m USING (work_order_id)
WHERE e.part_id = var_part_id
AND e.open = 'TRUE'
AND e.quantity_allocated IS NULL
AND e.quantity_expected >= e.quantity_actual
GROUP BY 1;
Superior view
Integrate this into the original query / view:
CREATE OR REPLACE VIEW view_production_parts AS
SELECT p.part_id, p.min_amount
,gp.part_num, gp.description, p.quantity_available
,p.quantity_total - p.quantity_available AS quantity_alloc
,p.quantity_total
,x.pending_amount
,p.production_run
,(ppur.purchased_part_id IS NOT NULL) AS is_purchased_part
,ppur.purchased_part_id, p.store_move_type_id, gp.part_status_id
,p.default_location
,COALESCE(pwoh.part_work_order_hold_id, 0) AS part_work_order_hold_id
FROM general_part gp
JOIN part p USING (part_id)
LEFT JOIN purchased_part ppur USING (part_id)
LEFT JOIN part_work_order_hold pwoh USING (part_id)
LEFT JOIN (
SELECT e.part_id
,GREATEST(COALESCE(sum(e.quantity_expected), 0)
- COALESCE(sum(m.total_stock_moved_out), 0)
, 0) AS pending_amount
FROM view_work_orders e
LEFT JOIN (
SELECT source_work_order_id AS work_order_id
,sum(quantity) AS total_stock_moved_out
FROM view_work_order_move_parts_details
WHERE part_id = var_part_id
GROUP BY 1
) m USING (work_order_id)
WHERE e.part_id = var_part_id
AND e.open = 'TRUE'
AND e.quantity_allocated IS NULL
AND e.quantity_expected >= e.quantity_actual
GROUP BY 1
) x USING (part_id)
ORDER BY gp.part_num;
Untested, obviously.
I realize SQL is not the best language for this, but this is a homework assignment to write a function that will take an argument N and will find the prime numbers (N=10,000,000) between 1 and 10 million. I am using Postgresql. Here is my attempt:
--First create table Numbers with all numbers from 1 to 10000000 in it
create table numbers(number bigint);
--Use this function to fill it in:
create or replace function populate(top bigint) RETURNS void as $$
declare
i bigint:=1;
begin
while(i<=top) LOOP
insert into numbers(number)
values(i);
i:=i+1;
END LOOP;
END; $$ LANGUAGE plpgsql;
--Function primes that returns all primes up to N
create or replace function primes(N bigint) RETURNS void AS $$
DECLARE
first bigint :=3;
last bigint :=2;
BEGIN
--create table t1 and insert all odd integers from 3 to N (and 2)
create table t1(a bigint);
INSERT into t1(a)
select number
from numbers
where (number%2 <> 0 or number = 2)
AND number<=N AND number<>1;
--Use Sieve of Erastothenes to find primes
while (last < sqrt(n)) LOOP
first:= (select * from t1 where a>last order by a limit 1);
last:= first* first;
--delete from list of primes all multiples of the primes in the range of first-last
-- (first run-through is primes in range of 3-9, second run-through would be primes in range of 11-121, etc.)
delete from t1
where a in (select n1.number * t.a
from t1 as t
inner join numbers as n1
on n1.number >= t.a
and n1.number<= n/t.a
where t.a>=first
and t.a<last);
END LOOP;
END; $$ LANGUAGE plpgsql;
A good review of the topic is here: https://sqlserverfast.com/blog/hugo/2006/09/the-prime-number-challenge-great-waste-of-time/
But for a homework problem, you should do your own work.
I don't think anyone actually checks or compares most of these postings - I've posted a couple of poor runners just to find out, but nobody called them. If you are inclined to compare though, you'll find this readable and fast:
IF (SELECT OBJECT_ID ('tempdb.dbo.#Numbers')) IS NOT NULL
DROP TABLE #Numbers;
CREATE TABLE #Numbers (Prime INT NOT NULL, Squared BIGINT PRIMARY KEY CLUSTERED);
DECLARE #MaxPrime INT = 1000000;
;WITH
GroupingDriver AS
(
SELECT CAST('7' AS BIGINT) as Interval
UNION ALL
SELECT Interval+30
FROM GroupingDriver
WHERE Interval+30 < #MaxPrime
)
INSERT INTO #Numbers
SELECT 2 AS 'Number', 4 AS 'SquareNo'
UNION ALL
SELECT 3 AS 'Number', 9 AS 'SquareNo'
UNION ALL
SELECT 5 AS 'Number', 25 AS 'SquareNo'
UNION ALL
SELECT Prime.Number, Prime.Number * Prime.Number
FROM GroupingDriver
CROSS APPLY ( VALUES (GroupingDriver.Interval),
(GroupingDriver.Interval+4),
(GroupingDriver.Interval+6),
(GroupingDriver.Interval+10),
(GroupingDriver.Interval+12),
(GroupingDriver.Interval+16),
(GroupingDriver.Interval+22),
(GroupingDriver.Interval+24) ) AS Prime(Number)
WHERE Prime.Number < #MaxPrime
OPTION (MAXRECURSION 0);
Now remove those divisible by other primes. We just used squared as a cutoff point for comparison.
SELECT Prime
FROM #Numbers n
WHERE NOT EXISTS (SELECT 1
FROM #Numbers AS p
WHERE p.Squared <= n.Prime
AND n.Prime % p.Prime = 0);
GO