SQL trigger with parameter - node.js

I have a nodejs app with SQL Server. I want to be able to update a table for a "specific org" based on an insert and delete action. Let's say I have 2 tables as follows:
Project: projId, orgId, projName
Tasks: taskId, projId, taskName
Users: userId, orgId, userName
OrganizationStats: numberOfProjects, numberOfUsers, numberOfTasks orgId
So let's say I add a new project for an organization where orgId = 1. My insert statement from Nodejs would be:
insert into project (projId, orgId, projName)
values (${'projId'}, ${'orgId'}, 'New Project');
I want to write a trigger in SQL Server that adds 1 to the numberOfProjects column with orgId that's passed in.
create trigger updateProjectAfterInsert
on project
after insert
as
begin
update OrganizationStats
set numprojects = numberOfProjects + 1
where orgId = 'THE_INSERTED_ORGID_VALUE';
end;
My problem is I don't know how to pass the ${'orgId'} to the trigger.

I'm going to expand on my comment here:
Personally, I recommend against storing values which can be calculated by an aggregate. If you need such information easily accessible, you're better off making a VIEW with the value in there, in my opinion.
What I mean by this is that NumProjects has "no right" being in the table OrganizationStats, instead it should be calculated at the time the information is needed. You can't use an aggregate function in a computed column's definition without a scalar function, and those can be quite slow. Instead I recommend creating a VIEW (or if you prefer table value function) to give you the information from the table:
CREATE VIEW dbo.vw_OrganisationStats AS
SELECT {Columns from OrganizationStats},
P.Projects AS NumProjects
FROM dbo.OrganizationStats OS
CROSS APPLY (SELECT COUNT(*) AS Projects
FROM dbo.Projects P
WHERE P.OrgID = OS.OrgID) P;
I use a CROSS APPLY with a subquery, as then you don't need a huge GROUP BY at the end.

I think what you want this something like this:
CREATE TRIGGER updateProjectAfterInsert
ON Project
AFTER INSERT
AS
BEGIN
UPDATE OrganizationStats
SET NumProjects = NumProjects + 1
WHERE OrgId IN (SELECT OrgId FROM inserted);
END;
Also note, Triggers must always assume multiple rows. It's possible to insert multiple rows, update multiple rows, and delete multiple rows. The "inserted" and "deleted" collections contain the data needed ("inserted" contains the rows being inserted, "deleted" contains the rows being deleted, and on an update "inserted" contains the values after the update, and "deleted" contains the values before the update).

Related

NetSuite Search formula for items that have no open transactions

I am trying to create a formula to obtain a list of items that have no open transactions.
I cant just filter out by status as this filters out transactions that are open, as opposed to showing me only items with nothing open.
So basically if an item has anything open then i dont want it on the search. I do need it on the search if it has all closed or it has no transactions at all.
Hoping someone can help put me in the right direction.
I am a little bit stuck at where to start with the formulas and tried a case formula.
You can use item saved search adding under criteria as "Transaction Fields-status-anyOf-select all closed/rejected/declined statuses" not in filter reason of saved search.
Thanks.
To get the value of non transaction items as well, You need to check the check box use expression under criteria in standard subtab use parens() with OR expression.
And add one more condition as "Transaction Fields-Internal Id-anyOf-none with
"Transaction Fields-status-anyOf-select all closed/rejected/declined statuses".
Add both condition with OR logic.
It will work for both items condition if it has transaction status with closed or with none of transaction internal ids.
Thanks.
I think this is possible in a saved search, and requires a change in the way the filtering is done. Rather than filtering on the "Filters", using grouping and summary calculations to determine if an item qualifies, basically :
Create the item saved search as you would normally, but don't include a "Standard" filter for the openness of the transaction.
In the results, group by item name (or internalid), and another fields you want to include in the top-level results.
In the Criteria - Summary list, add a Formula (Number) condition :
Summary Type= Sum (Count won't work here)
Formula = case when {transaction.status} = 'Open' then 1 else 0 end
Equal to 0
Whether this is more or less elegant than bknight's answer is debatable.
I don't think this is the sort of thing you can do with a single saved search.
It would be fairly easy to do with SuiteQL though.
The script below runs in the console and finds items that are not on any Pending Billing Sales Orders. It's adapted from a script with a different purpose but illustrates the concept.
You can get a list of the status values to use by creating a saved search that finds all the transactions with open statuses you want to exclude , take note of that saved search's id and running the second script in the console
require(['N/query'], query => {
const sqlStr = `
select item.id, itemid, count(po.tranid) as po, count(bill.tranId) as bill, max(bill.tranDate) as lastBilled, count(sale.tranId) as sales, count(tran.tranId) as trans
from item
left outer join transactionLine as line
on line.item = item.id
left outer join transaction as tran on line.transaction = tran.id
left outer join transaction as po on line.transaction = po.id and po.type = 'PurchOrd'
left outer join transaction as bill on line.transaction = bill.id and bill.type = 'VendBill'
left outer join transaction as sale on line.transaction = sale.id and sale.type in ('CustInvc', 'CashSale')
where item.id not in (select otl.item from transactionLine otl, transaction ot where
otl.transaction = ot.id and ot.status in ('SalesOrd:F'))
group by item.id, item.itemid
`;
console.log(sqlStr);
console.log(query.runSuiteQL({
query: sqlStr
}).asMappedResults().map((r, idx)=>{
if(!idx) console.log(JSON.stringify(r));
return `${r.id}\t${r.itemid}\t${r.po}\t${r.bill}\t${r.lastBilled}\t${r.sales}\t${r.trans}`;
}).join('\n'));
});
require(['N/search'], search=>{
const filters = search.load({id:304}).filters;
console.log(JSON.stringify(filters.find(f=>f.name == 'status'), null, ' '));
});
In terms of doing something with this you could run this in a saved search and email someone the results, show the results in a workbook in SuiteAnalytics or build a portlet to display the results - for this last Tim Dietrich has a nice write up on portlets and SuiteQL

How to query fields with multiple values in Azure Cognitive Search

Working on Azure Cognitive Search with backend as MS SQL table, have some scenarios where need help to define a query.
Sample table structure and data :
Scenarios 1 : Need to define a query which will return data based on category.
I have tied query using search.ismatch but its uses prefix search and matches other categories as well with similar kind of values i.e. "Embedded" and "Embedded Vision"
$filter=Region eq 'AA' and search.ismatch('Embedded*','Category')
https://{AZ_RESOURCE_NAME}.search.windows.net/indexes/{INDEX_NAME}/docs?api-version=2020-06-30-Preview&$count=true&$filter=Region eq 'AA' and search.ismatch('Embedded*','Category')
And it will response with below result, where it include "Embedded" and "Embedded Vision" both categories.
But my expectation is to fetch data only if it match "Embedded" category, as highlighted below
Scenario 2: For the above Scenario 1, Need little enhancement to find records with multiple category
For example if I pass multiple categories (i.e. "Embedded" , "Automation") need below highlighted output
you'll need to use a different analyzer which will break the tokens on every ';' just for the category field rather than 'whitespaces'.
You should first ensure your Category data is populated as a Collection(Edm.String) in the index. See Supported Data Types in the official documentation. Each of your semicolon-separated values should be separate values in the collection, in a property called Category (or similar).
You can then filter by string values in the collection. See rules for filtering string collections. Assuming that your index contains a string collection field called Category, you can filter by categories containing Embedded like this:
Category/any(c: c eq 'Embedded')
You can filter by multiple values like this:
Category/any(c: search.in(c, 'Embedded, Automation'))
Start with clean data in your index using proper types for the data you have. This allows you to implement proper facets and you can utilize the syntax made specifically for this. Trying to work around this with wildcards is a hack that should be avoided.
To solve above mention problem used a below SQL function which will convert category to a json string array supported by Collection(Edm.String) data type in Azure Search.
Sql Function
CREATE FUNCTION dbo.GetCategoryAsArray
(
#ID VARCHAR(20)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #result NVARCHAR(MAX) = ''
SET #result = REPLACE(
STUFF(
(SELECT
','''+ TRIM(Value) + ''''
FROM dbo.TABLEA p
CROSS APPLY STRING_SPLIT (Category, ';')
WHERE p.ID = #ID
FOR XML PATH('')
),1,1,''),'&','&')
RETURN '[' + #result + ']'
END
GO
View to use function and return desired data
CREATE View dbo.TABLEA_VIEW AS
select
id
,dbo. GetCategoryAsArray(id) as CategoryArr
,type
,region
,Category
from dbo.TABLEA
Defined a new Azure Search Index using above SQL View as data source and during Index column mapping defined CategoryArr column as Collection(Edm.String) data type
Query to use to achieve expected output from Azure Search
$filter=Region eq 'AA' and CategoryArr/any(c: search.in(c, 'Embedded, Automation'))

Writing a subquery to display records in a grid

I have two DAC's POReceipt, and and POReceiptLine. POReceiptLine containts a field called MfrPartNbr.
I want the user to be able to lookup all the POReceipts where the POReceiptLine.MfrPartNbr is equal to an entered value.
The SQL would be
SELECT *
FROM dbo.POReceipt
WHERE POReceipt.ReceiptNbr IN
(
SELECT ReceiptNbr
FROM dbo.POReceiptLine
WHERE MfrPartNbr = 'MY_ENTERED_PART_NBR'
)
Any idea how to write the BQL Statement for this?
As stated, an inner join won't work in this case because you will receive the same POReceipt multiple times (once for each POReceiptLine). The following BQL query shows how you can get the desired results using a sub query. If mfrPartNbr is an extension field, then replace POReceiptLine.mfrPartNbr with the correct extension name (e.g. POReceiptLineExtension.mfrPartNbr).
PXSelect<POReceipt, Where<Exists<
Select<POReceiptLine,
Where<POReceiptLine.receiptNbr, Equal<POReceipt.receiptNbr>,
And<POReceiptLine.mfrPartNbr, Equal<Required<POReceiptLine.mfrPartNbr>>>>>>>>.Select(this, "MY_ENTERED_PART_NBR");

how to update a non updatable view?

I'm working on odoo version11. I created a view named test. I upgraded the module. Later I need to make some changes including changing some values and removing some values. Whenever I try to upgrade the module I get the error
psycopg2.OperationalError: cannot delete from view "test" DETAIL:
Views containing GROUP BY are not automatically updatable. HINT: To
enable deleting from the view, provide an INSTEAD OF DELETE trigger or
an unconditional ON DELETE DO INSTEAD rule.
How to provide the rule? My code is as follows
#api.model_cr
def init(self):
cr = self.env.cr
tools.drop_view_if_exists(cr, 'o_test')
cr.execute("""
CREATE or replace view o_test as
(
SELECT
mve.id as id,
acc.code as account_code,
SUM (mve.debit-mve.credit) AS balance,
mve.account_id as account_id
FROM account_move_line mve
LEFT JOIN account_account acc ON mve.account_id = acc.id
GROUP BY mve.id,mve.account_id,acc.code
)
""")
I want to change the code to as follows:
#api.model_cr
def init(self):
cr = self.env.cr
tools.drop_view_if_exists(cr, 'o_test')
cr.execute("""
CREATE or replace view o_test as
(
SELECT
mve.account_id as id,
acc.code as account_code,
SUM (mve.debit-mve.credit) AS balance,
mve.account_id as account_id
FROM account_move_line mve
LEFT JOIN account_account acc ON mve.account_id = acc.id
GROUP BY mve.account_id,acc.code
)
""")
Editable Views can be created in Postgres using Rules and GROUP BY clause should not be a problem. You can write rule like the below for update query. Here I have updated only one column
CREATE OR REPLACE RULE update_o_test AS
ON UPDATE TO o_test
DO INSTEAD
UPDATE account_account SET code = new.code
WHERE account_account.id = old.id;
Similarly you can create rules for Insert and Delete operations. Adding rules after the end of your create view statement should work.
Hope this helps...

Inserting data into database with python/sqlite3 by recognising the column name

I've got a problem that I don't know how to solve, I've tried many solutions but always getting that Operational error: near...
def insert_medicine_to_table():
con = sqlite3.connect('med_db3.db')
cur = con.cursor()
table_name = 'medicines'
column_name = "présentation"
value = 'Boîte de 2 seringues pré-remplies'
cur.execute("INSERT INTO medicines {} VALUES (?)".format(column_name), value)
con.commit()
con.close()
sqlite3.OperationalError: near "présentation": syntax error
The goal here is that either the script or python has to recognize the field (column name) and insert the value into "that" field, like the following:
fields = ['présentation', 'princeps', 'distributeur_ou_fabriquant', 'composition', 'famille', 'code_atc', 'ppv', 'prix_hospitalier', 'remboursement', 'base_de_remboursement__ppv', 'nature_du_produit']
values = ['Boîte de 2 seringues pré-remplies', 'Oui', 'SANOFI', 'Héparine', 'Anticoagulant héparinique', 'B01AB01', '43.80', '27.40', 'Oui', '43.80', 'Médicament']
That is one entry in the database. The problem here is that other entries can or not have one or more values for some field, and also the fields are not presented in the same order in other entries.
It has to recognize each field in the database table and insert each value into the right column.
The problem causing your error is that your SQL isn't valid. The statement you are trying to execute is:
INSERT INTO medicines présentation VALUES (?)
The statement you want to execute is:
INSERT INTO medicines ("présentation") VALUES (?)
As far as your larger question is concerned, if you create both the list of columns ("présentation") and list of parameter markers (?) and build the query using them, you're most of the way there.
If a field can have multiple values supplied for each "entry" in your database, you may need to change your database design to handle that. You'll at least need to figure out how you want to handle the situation, but that would be a matter for a different question.

Resources