I am needing help converting a PostgreSQL query to MSSQ.
Below is what i have done so far but i am issuing with the function and array areas which i do not think are allowed in MS SQL.
Is there something that that i need to do change the function and looks the WHERE statement has an array in it too.
I have added the select statement for the #temp table but when i create the #temp table i am getting errors saying incorrect syntax
CREATE FUNCTION pm_aggregate_report
(
_facility_ids uuid[]
, _risk_ids uuid[] DEFAULT NULL::uuid[]
, _assignee_ids uuid[] DEFAULT NULL::uuid[]
, _start_date date DEFAULT NULL::date
, _end_date date DEFAULT NULL::date
)
RETURNS TABLE
(
facility character varying
, pm_id uuid, grouped_pm boolean
, risk_id uuid
, risk character varying
, pm_status_id uuid
, user_id uuid
, assignee text
, completed_by uuid
, total_labor bigint
)
CREATE TABLE #tmp_pm_aggregate
(
facility_id VARCHAR(126),
pm_id VARCHAR(126),
grouped_pm VARCHAR(126),
risk_id VARCHAR(126),
pm_status_id VARCHAR(126),
user_id VARCHAR(126),
completed_by VARCHAR(126)
)
SELECT DISTINCT
COALESCE(gp.facility_id, a.facility_id) as facility_id,
COALESCE(p.grouped_pm_id, p.id) as pm_id,
CASE WHEN p.grouped_pm_id IS NULL THEN false ELSE true END as grouped_pm,
COALESCE(gp.risk_id, a.risk_id) as risk_id,
COALESCE(gp.pm_status_id, p.pm_status_id) as pm_status_id,
COALESCE(gass.user_id, sass.user_id) as user_id,
COALESCE(gp.completed_by, p.completed_by) as completed_by
FROM pms p
JOIN assets a
ON p.asset_id = a.id
LEFT JOIN grouped_pms gp
ON p.grouped_pm_id = gp.id
LEFT JOIN assignees sass
ON p.id = sass.record_id
AND sass.type = 'single_pm'
LEFT JOIN assignees gass
ON p.grouped_pm_id = gass.record_id
AND gass.type = 'grouped_pm'
LEFT JOIN users u
ON (sass.user_id = u.id OR gass.user_id = u.id)
WHERE a.facility_id = ANY(_facility_ids)
AND NOT a.is_component
AND COALESCE(gp.pm_status_id, p.pm_status_id) in ('f9bdfc17-3bb5-4ec0-8477-24ef05ea3b9b', '06fc910c-3d07-4284-8f6e-8fb3873f5333')
AND COALESCE(gp.completion_date, p.completion_date) BETWEEN COALESCE(_start_date, '1/1/2000') AND COALESCE(_end_date, '1/1/3000')
AND COALESCE(gp.show_date, p.show_date) <= CURRENT_TIMESTAMP
AND COALESCE(gass.user_id, sass.user_id) IS NOT NULL
AND u.user_type_id != 'ec823d98-7023-4908-8006-2e33ddf2c11b'
AND (_risk_ids IS NULL OR COALESCE(gp.risk_id, a.risk_id) = ANY(_risk_ids)
AND (_assignee_ids IS NULL OR COALESCE(gass.user_id, sass.user_id) = ANY(_assignee_ids);
SELECT
f.name as facility,
t.pm_id,
t.grouped_pm,
t.risk_id,
r.name as risk,
t.pm_status_id,
t.user_id,
u.name_last + ', ' + u.name_first as assignee,
t.completed_by,
ISNULL(gwl.total_labor, swl.total_labor) as total_labor
FROM #tmp_pm_aggregate t
JOIN facilities f
ON t.facility_id = f.id
JOIN risks r
ON t.risk_id = r.id
JOIN users u
ON t.user_id = u.id
LEFT JOIN (SELECT wl.record_id, wl.user_id, SUM(wl.labor_time) as total_labor
FROM work_logs wl
WHERE wl.type = 'single_pm'
GROUP BY wl.record_id, wl.user_id) as swl
ON t.pm_id = swl.record_id
AND t.user_id = swl.user_id
AND t.grouped_pm = false
LEFT JOIN (SELECT wl.record_id, wl.user_id, SUM(wl.labor_time) as total_labor
FROM work_logs wl
WHERE wl.type = 'grouped_pm'
GROUP BY wl.record_id, wl.user_id) as gwl
ON t.pm_id = gwl.record_id
AND t.user_id = gwl.user_id
AND t.grouped_pm = true
ORDER BY facility,
assignee,
risk;
DROP TABLE #tmp_pm_aggregate;
You can create an inline Table Valued Function, and simply return a resultset from it. You do not need (and cannot use) a temp table, you do not declare the returned "rowset" shape.
For the array parameters, you can use a Table Type:
CREATE TYPE dbo.GuidList (value uniqueidentifier NOT NULL PRIMARY KEY);
Because the table parameters are actual tables, you must query them like this (NOT EXISTS (SELECT 1 FROM #risk_ids) OR ISNULL(gp.risk_id, a.risk_id) IN (SELECT r.value FROM #risk_ids))
The parameters must start with #
There is no boolean type, you must use bit
Always use deterministic date formats for literals. yyyymmdd works for dates. Do you need to take into account hours and minutes, because you haven't?
ISNULL generally performs better than COALESCE in SQL Server, as the compiler understands it better
You may want to pass a separate parameter showing whether you passed in anything for the optional table parameters
I suggest you look carefully at the actual query: why does it need DISTINCT? It performs poorly, and is usually a code-smell indicating poorly thought-out joins. Perhaps you need to combine the two joins on assignees, or perhaps you should use a row-numbering strategy somewhere.
CREATE FUNCTION dbo.pm_aggregate_report
(
#facility_ids dbo.GuidList
, #risk_ids dbo.GuidList
, #assignee_ids dbo.GuidList
, #start_date date
, #end_date date
)
RETURNS TABLE AS RETURN
SELECT DISTINCT -- why DISTINCT, perhaps rethink your joins
ISNULL(gp.facility_id, a.facility_id) as facility_id,
ISNULL(p.grouped_pm_id, p.id) as pm_id,
CASE WHEN p.grouped_pm_id IS NULL THEN CAST(0 AS bit) ELSE CAST(1 AS bit) END as grouped_pm,
ISNULL(gp.risk_id, a.risk_id) as risk_id,
ISNULL(gp.pm_status_id, p.pm_status_id) as pm_status_id,
ISNULL(gass.user_id, sass.user_id) as user_id,
ISNULL(gp.completed_by, p.completed_by) as completed_by
FROM pms p
JOIN assets a
ON p.asset_id = a.id
LEFT JOIN grouped_pms gp
ON p.grouped_pm_id = gp.id
LEFT JOIN assignees sass
ON p.id = sass.record_id
AND sass.type = 'single_pm'
LEFT JOIN assignees gass
ON p.grouped_pm_id = gass.record_id
AND gass.type = 'grouped_pm'
LEFT JOIN users u
ON (sass.user_id = u.id OR gass.user_id = u.id) -- is this doubling up your rows?
WHERE a.facility_id IN (SELECT f.value FROM #facility_ids f)
AND a.is_component = 0
AND ISNULL(gp.pm_status_id, p.pm_status_id) in ('f9bdfc17-3bb5-4ec0-8477-24ef05ea3b9b', '06fc910c-3d07-4284-8f6e-8fb3873f5333')
AND ISNULL(gp.completion_date, p.completion_date) BETWEEN ISNULL(#start_date, '20000101') AND ISNULL(#end_date, '30000101') -- perhaps use >= AND <
AND ISNULL(gp.show_date, p.show_date) <= CURRENT_TIMESTAMP
AND ISNULL(gass.user_id, sass.user_id) IS NOT NULL
AND u.user_type_id != 'ec823d98-7023-4908-8006-2e33ddf2c11b'
AND (NOT EXISTS (SELECT 1 FROM #risk_ids) OR ISNULL(gp.risk_id, a.risk_id) IN (SELECT r.value FROM #risk_ids))
AND (NOT EXISTS (SELECT 1 FROM #assignee_ids) OR ISNULL(gass.user_id, sass.user_id) IN (SELECT aid.value FROM #assignee_ids aid));
I am writing an INNER JOIN that added fields like CompanyId etc..
But in my CASE inside the INNER JOIN I am getting an error after the WHERE
The error say on the IR.MyId and ICB1.MyId
"multi-part identifier could not be bound"
My intention for this query is to add a CASE inside the INNER JOIN.
So the first column is A.CompanyId. The second column will be the CASE statement to check if the ID exists in another table. If not, it will do another SELECT statement, ELSE just MyID.
But I get an error on the SELECT COUNT(*) inside the CASE WHEN.
Any help is appreciated. Thanks.
INNER JOIN
(SELECT DISTINCT
A.CompanyId,
CASE
WHEN (SELECT count(*)
FROM Table1 IBC1
WHERE IR.MyId = ICB1.MyId) == 0
THEN 1
ELSE 0
END
FROM
cmp.CompanyNews A) E ON NP.CompanyId = E.CompanyId
I don't have your full query, but if it's viable I'd take the following approach to this:
...
CASE
WHEN ICB1_ID.ID IS NULL
THEN 1
ELSE 0
END [ColumnName]
FROM
...
INNER JOIN
(
SELECT DISTINCT A.CompanyId
FROM cmp.CompanyNews A
) E
ON NP.CompanyId = E.CompanyId
OUTER APPLY
(
SELECT TOP 1 ICB1.MyID ID
FROM Table1 IBC1
WHERE ICB1.MyId = IR.MyId
) ICB1_ID
Does a CASE statement that has multiple parts that is part of an INSERTstatement execute in order and do the 'rules' for lack of a better word stay in place even after the next line? in the query below, does the PO_TYPE assignment overrule the next command - to look in a list of articles for example? So even if that article was in the list in the second part of the statement if it was type 05 or 07 it will still assign to Andrew?
Thanks.
/*INSERT values into the table using SELECT making sure to exclude vendor 20800 - (see last line of code)*/
INSERT INTO SCM_PO_EMPLOYEE_NAME (PO_NUMBER, PO_ITEM_NUMBER, MATERIAL, BUSINESS_UNIT_CODE,PO_TYPE,TEAM_MEMBER_NAME)
SELECT I.PO_NUMBER,
I.PO_ITEM_NUMBER,
I.MATERIAL,
B.BU_CODE,
H.PO_TYPE,
CASE WHEN H.PO_TYPE IN ('05','07') -- Promo PO type - should be on both po type and stock category
AND I.STOCK_CATEGORY LIKE ('A60383%') -- stock category is second part of the check
THEN 'AZ'
WHEN H.PO_TYPE = '02' -- ma PO type
THEN 'MB'
WHEN I.MATERIAL IN ( SELECT ARTICLE
FROM ADI_USER_MAINTAINED.dbo.SCM_EMPLOYEE_ARTICLE A ) -- Check the Employee to article table next
THEN A.TEAM_MEMBER_NAME -- If the PO number matches that conditions then assign the employee from the employee article table
WHEN M.BUSINESS_UNIT_CODE = B.BU_CODE -- if not use then go to the BU assignment (below)
THEN B.TEAM_MEMBER_NAME --- Use the team member name from the Employee_BU table
END AS [TEAM_MEMBER_NAME]
FROM PDX_SAP_USER.dbo.VW_PO_HEADER H
JOIN PDX_SAP_USER.dbo.VW_PO_ITEM I ON H.PO_NUMBER = I.PO_NUMBER
JOIN PDX_SAP_USER.dbo.VW_MM_MATERIAL M ON I.MATERIAL = M.MATERIAL
JOIN ADI_USER_MAINTAINED.dbo.SCM_EMPLOYEE_ARTICLE A ON I.MATERIAL = A.ARTICLE
JOIN ADI_USER_MAINTAINED.dbo.SCM_EMPLOYEE_BU B ON B.BU_CODE = M.BUSINESS_UNIT_CODE
WHERE H.VENDOR_NO <> '20800'; --Exclude '20800' as a vendor!!
A case expression is evaluated sequentially.
So, the second then is only evaluated when the first then does not return true. As an extreme example of this, consider:
select (case when 1=1 then 'true'
when 1/0 = 0 then 'error'
end)
This returns 'true' instead of error'ing out.
I am trying to return a string result when a value IS NULL in SQL Server.
An example of what I am doing is below. This is a sub select query...
SELECT
stockitems.code AS StockCode,
stockitems.description AS
ShortDescription,
(SELECT
CASE remaining
WHEN NULL
THEN 'NO LEVEL'
ELSE remaining
END
FROM StockItemAlertLevels
WHERE StockItems.ID = StockItemAlertLevels.StockItemID) AS RemainingLevel
FROM
stockitems
WHERE
StockItems.Attribute1 = '1'
ORDER BY
StockItems.Code
No matter what I do, I cannot get the output result of the subselect to change if the result is NULL. Not all returned results from the SELECT statement will have returned results in the subselect - this is where I need the set the output to "NO LEVEL". The subselect will never return more than one result.
Hope this makes sense.
I think you want COALESCE():
SELECT si.code As StockCode, si.description AS ShortDescription,
(SELECT COALESCE(remaining, 'NO LEVEL')
FROM StockItemAlertLevels sal
WHERE si.ID = sal.StockItemID
) as RemainingLevel
FROM stockitems si
WHERE si.Attribute1 = '1'
ORDER BY si.Code;
I suspect, though, that your problem is not that remaining is NULL, but that there are no matches. You can move the COALESCE() outside the subquery, but I would propose a LEFT JOIN:
SELECT si.code As StockCode, si.description AS ShortDescription,
COALESCE(remaining, 'NO LEVEL') as RemainingLevel
FROM stockitems si LEFT JOIN
StockItemAlertLevels sal
ON si.ID = sal.StockItemID
WHERE si.Attribute1 = '1'
ORDER BY si.Code;
In my query, I'm trying to assign [Rcv-Qty] column that is created as template column. I need to assign that value to the intended row but I cannot group by because of this column is not member of the table. That's why I can't match my result to the intended row.
The result copies itself to all rows, not only the intended row that I need.
Here is my query.
SELECT
mto.POS as [POS], mto.MaterialID as [Ident No],
mat.MaterialUnitDesc as [Description], mto.QuantityUnit as [Unit],
mto.Quantity as [Mto-QTY], req.Quantity as [Req-QTY],
(SELECT SUM(inv.InvoiceQTYTakenCount)
FROM INVOICE as inv
WHERE inv.InvoiceNo != ''
AND inv.InvoiceNo != 'UNKNOWN'
AND inv.IsometricID = '24200076B20-TK1-0001_0') as [Rcv-Qty],
'' as [Inv-QTY]
FROM
MTO_ISOMETRIC as mto
INNER JOIN
MATERIAL_LIST as mat ON mto.MaterialID = mat.MaterialID
INNER JOIN
REQUEST as req ON mto.RequestID = req.RequestID
INNER JOIN
INVOICE as inv ON mto.InvoiceID = inv.InvoiceID
WHERE
req.RequestNo = 369
AND req.IsometricID = '24200076B20-TK1-0001_0'
AND (inv.InvoiceNo = '' OR inv.InvoiceNo = 'UNKNOWN')
GROUP BY
mto.POS, mto.MaterialID, mat.MaterialUnitDesc, mto.QuantityUnit,
mto.Quantity, req.Quantity
ORDER BY
mto.POS ASC
The result is in the image:
[Rcv-Qty] column result must be seen in the first record, and the others must be NULL value. (Because of there is no information for them in database.)
Any ideas?