Passing parameter to a table-valued function - function

I am trying to pass a parameter value to a table-valued function which has four parameters and "returns table". However, I receive following error when I pass a parameter value to one of its varchar parameter:
Msg 8114, Level 16, State 5, Line 6 Error converting data type varchar
to bigint.
declare #Scenario1 as varchar(30)
set #Scenario1 = '2017_B01'
select *
From [dbo].[fn_GetAEAssumptionFacts](#Scenario1,null,null,null) fng
Glimpse at function:
CREATE FUNCTION [dbo].[fn_GetAEAssumptionFacts]
(
#pScenarioName varchar(500) = NULL
,#pBuildingID varchar(500) = NULL
,#pLeaseID varchar(500) = NULL
,#pTenantName varchar(500) = NULL
)
RETURNS TABLE
AS
RETURN
select
.....
from ae11.dbo.rvw_FinancialLineItems fli
....
INNER JOIN ae11.dbo.rvw_Scenarios s on s.Id = pas.ScenarioId
left join
(select
externalID,
PropertyAssetId,
LeaseID,
BeginDate
from ae11.dbo.ivw_Leases
WHERE PropertyAssetID IN
(select ID from AE11.dbo.PropertyAssets where scenarioID =
(CASE WHEN isnull(#pScenarioName, '') = ''
THEN (select ID from AEX.[dbo].[ConfigurationFieldTable]
where [Type] = 'Lease Connect Current Scenario' )
ELSE #pScenarioName
END)
)
) lea
ON lea.LeaseID = uni.ExternalID
AND lea.PropertyAssetID = uni.PropertyAssetId
where 1=1
......
AND s.id = (CASE WHEN isnull(#pScenarioName, '') = ''
THEN (select ID from AEX.[dbo].[ConfigurationFieldTable]
where [Type] = 'Lease Connect Current Scenario' )
ELSE #pScenarioName
END)

Here
(CASE WHEN isnull(#pScenarioName, '') = ''
THEN (select ID from AEX.[dbo].[ConfigurationFieldTable]
where [Type] = 'Lease Connect Current Scenario' )
ELSE #pScenarioName
END)
You are taking a value depending on #ScenarioName. This will either be the result of select ID from AEX.[dbo].[ConfigurationFieldTable] WHERE... or the content of #ScenarioName.
I assume, that this ID is a bigint, while your #SenarioName is a string. And the s.ID you want to compare it against - I don't know...
But - to be honest - my magic crystall ball is out for cleaning and the information you provide is not enough.

Related

SQL Server OpenJson - retrieve row based on nested Json, querying multiple Json rows

Given the data table below, how can I retrieve only the row #3, querying the field "chave", based on multiple json rows?
I want to retrieve the master row where the json field (NomeCampo = id and Valor = 3) and also (NomeCampo = id2 and Valor = 5)
id id_modulo chave
624D4FB5-6197-11EA-A947-9C5C8ED7177E 17 [{"NomeCampo":"id","Valor":2},{"NomeCampo":"id2","Valor":5}]
4CF95795-4BFD-EC11-8CE5-80A589B639E0 17 [{"NomeCampo":"id","Valor":3},{"NomeCampo":"id2","Valor":4}]
DBE9275A-9BFF-EC11-8CE5-80A589B639E0 17 [{"NomeCampo":"id","Valor":3},{"NomeCampo":"id2","Valor":5}]
BE3228C6-9BFF-EC11-8CE5-80A589B639E0 17 [{"NomeCampo":"id","Valor":3},{"NomeCampo":"id2","Valor":6}]
This is the SQL that I have but it is not retrieving any row at all:
SELECT id, id_modulo, chave
FROM myTable
WHERE id_modulo = 17
AND EXISTS (SELECT *
FROM OPENJSON(chave)
WITH (NomeCampo nvarchar(max) '$.NomeCampo',
Valor nvarchar(max) '$.Valor') AS [Info]
WHERE ([Info].NomeCampo = 'id' AND [Info].Valor = '3')
AND ([Info].NomeCampo = 'id2' AND [Info].Valor = '5'))
Is this even possible to do?
This is actually a case of relational division. You need to find the set of JSON rows which have these properties, so you need to group it
SELECT
t.id,
t.id_modulo,
t.chave
FROM myTable t
WHERE t.id_modulo = 17
AND EXISTS (SELECT 1
FROM OPENJSON(t.chave) WITH (
NomeCampo nvarchar(100),
Valor nvarchar(1000)
) AS Info
WHERE (Info.NomeCampo = 'id' AND Info.Valor = '3'
OR Info.NomeCampo = 'id2' AND Info.Valor = '5')
GROUP BY ()
HAVING COUNT(*) = 2
);
Another option
SELECT
t.id,
t.id_modulo,
t.chave
FROM myTable t
WHERE t.id_modulo = 17
AND EXISTS (SELECT 1
FROM OPENJSON(t.chave) WITH (
NomeCampo nvarchar(100),
Valor nvarchar(1000)
) AS Info
GROUP BY ()
HAVING COUNT(CASE WHEN Info.NomeCampo = 'id' AND Info.Valor = '3' THEN 1 END) > 0
AND COUNT(CASE WHEN Info.NomeCampo = 'id2' AND Info.Valor = '5' THEN 1 END) > 0
);

I want to throw an exception when my mysql stored procedure doesn't find any results

I'm having trouble finding a way to throw an exception from a stored procedure if no data is found.
I need a way to check the query results in the if statement.
If the count is 0 then throw exception. If there's data then return the data.
CREATE DEFINER=`adminHC`#`%` PROCEDURE `find_user_with_credentials`(
IN `usernameIN` VARCHAR(45)
, IN `encryptedpwIN` VARCHAR(45)
)
BEGIN
SELECT *
FROM (SELECT distinct
userid
,firstname
,lastname
,publicname
,email
,addressid
,create_date
,update_date
,active
FROM dev_users
WHERE (email = usernameIN OR (
publicname IS NOT null AND
publicname = usernameIN
))
AND encryptedpw = encryptedpwIN
AND active = 1) user;
IF count(user) > 0 THEN
SELECT user;
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = "Invalid username or password",
MYSQL_ERRNO = 403;
END IF;
END
Start with a SELECT COUNT(*) query and check the result of that.
CREATE DEFINER=`adminHC`#`%` PROCEDURE `find_user_with_credentials`(
IN `usernameIN` VARCHAR(45)
, IN `encryptedpwIN` VARCHAR(45)
)
BEGIN
IF (SELECT COUNT(*)
FROM dev_users
WHERE (email = usernameIN OR (
publicname IS NOT null AND
publicname = usernameIN
))
AND encryptedpw = encryptedpwIN
AND active = 1) > 0
THEN SELECT
userid
,firstname
,lastname
,publicname
,email
,addressid
,create_date
,update_date
,active
FROM dev_users
WHERE (email = usernameIN OR (
publicname IS NOT null AND
publicname = usernameIN
))
AND encryptedpw = encryptedpwIN
AND active = 1;
ELSE
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = "Invalid username or password",
MYSQL_ERRNO = 403;
END IF;
END
There's also no need for the SELECT * wrapper. And I doubt you need SELECT DISTINCT, since I assume you can't have multiple rows with the same userid, and there's nothing in the query that will duplicate rows.
If the query were too expensive to do twice, you could do it once and save the results in a temporary table. Then you can use SELECT COUNT(*) FROM temp_table to check if there are any results, and finally SELECT * FROM temp_table to return the data.

SQL Convert a char to boolean

I have in my table one row with a char value. When the value is NULL then a false should be outputted. If the value is not NULL then a true should be outputted.
So when I try to set user_group.tUser to 0 or 1 then I'm getting this error:
Invalid column name 'false'.
Invalid column name 'true'.
SELECT COALESCE((SELECT name
FROM v_company
WHERE companyId = userView.companyId), ' ') AS company,
userView.value AS companyUser,
userView.display AS displayedUser,
CASE
WHEN user_group.tUser IS NULL THEN 0
ELSE 1
END AS userIsMemberOfGroup
FROM v_user userView
LEFT OUTER JOIN cr_user_group user_group
ON ( user_group.group = 'Administrators'
AND user_group.tUser = userView.value )
ORDER BY company ASC,
displayedUser ASC
I think this is the logic you want:
SELECT COALESCE(v.name, ' ') as company,
u.value as companyUser, u.display as displayedUser,
(EXISTS (SELECT 1
FROM cr_user_group ug
WHERE ug.group = 'Administrators' AND
ug.tUser = uv.value
)
) as userIsMemberOfGroup
FROM v_user u LEFT JOIN
v_company c
ON c.companyId = v.companyId
ORDER BY company ASC, displayedUser ASC ;
In general, MySQL is very flexible about going between booleans and numbers, with 0 for false and 1 for true.
You can use MySQL IF function to return 'false' when name IS NULL, else 'true':
SELECT IF(name IS NULL, 'false', 'true')
FROM table;
A simple CASE expression would work here:
SELECT
name,
CASE WHEN name IS NOT NULL THEN true ELSE false END AS name_out
FROM yourTable;
We could also shorten the above a bit using IF:
IF(name IS NOT NULL, true, false)
SELECT
CASE
WHEN name IS NULL THEN 'false'
ELSE 'true'
END
FROM
table1;

MySQL Multiple Case When Exists Statement

I have two tables. Let's call it: SEATS and SEAT_ALLOCATION_RULE table.
Below are the table schema:
CREATE TABLE IF NOT EXISTS `SEATS` (
`SeatID` int(11) NOT NULL AUTO_INCREMENT,
`SeatName` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`SeatID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `SEATS` (`SeatID`, `SeatName`) VALUES
(1, 'Super VIP'),
(2, 'VIP'),
(3, 'Business'),
(4, 'Economy'),
(5, 'Standing');
CREATE TABLE IF NOT EXISTS `SEAT_ALLOCATION_RULE` (
`SeatID` int(11) NOT NULL DEFAULT '0',
`Origin` varchar(50) NOT NULL DEFAULT '0',
`Destination` varchar(50) NOT NULL DEFAULT '',
`Passenger_Type` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`SeatID`,`Origin`,`Destination`,`Passenger_Type`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `SEAT_ALLOCATION_RULE` (`SeatID`, `Origin`, `Destination, `Passenger_Type`) VALUES
(1, 'Malaysia','',''),
(2, 'Malaysia','Singapore',''),
(3, 'Malaysia','Singapore','Senior_Citizen'),
(4, 'Bangkok','Japan','Student'),
(5, 'Cambodia','China','Senior_Citizen');
SEAT_ALLOCATION_RULE table determines which seat should a passenger be assigned to based on the following order in priority:
1. Origin, destination, and passenger_type match
2. Origin and destination match
3. Origin match
It means that if all the fields (origin, destination, and passenger_type) match, it should take higher priority than if it is just two fields match and so on. If a column is empty, it is considered as unspecified and hence has lower priority. So, for example:
If the Origin is Malaysia, Destination is Singapore, and Passenger_Type is Senior_Citizen, it should return seatID 3
If the Origin is Malaysia, Destination is Singapore, and Passenger_Type is Student, it should return seatID 2 (since it only match Origin and Destination)
If the Origin is Malaysia, Destination is US, and Passenger_Type is Student, it should return seatID 1 (since it only match Origin).
Now, based on the rules above, if the origin is Malaysia, destination is Singapore, and Passenger_Type is student, the query to return seatID is as follow:
SELECT s.SeatID, s.SeatName
FROM SEATS s
WHERE
CASE WHEN EXISTS(
select 1
from SEAT_ALLOCATION_RULE r
where s.SeatID = r.SeatID
AND r.Origin = 'Malaysia'
AND r.Destination = 'Singapore'
AND r.Passenger_Type='Student') Then 1
WHEN EXISTS(
select 1
from SEAT_ALLOCATION_RULE r
where s.SeatID = r.SeatID
AND r.Origin = 'Malaysia'
AND r.Destination = 'Singapore'
AND r.Passenger_Type='') Then 1
WHEN EXISTS(
select 1
from SEAT_ALLOCATION_RULE r
where s.SeatID = r.SeatID
AND r.Origin = 'Malaysia'
AND r.Destination = ''
AND r.Passenger_Type='') Then 1 END
However, the query above does not work as it will return seatID 1 and 2, but the expected output is only seatID 2 (since origin and destination matches and it takes higher precedence). Can someone help to correct my SQL query?
This should do the trick:
select seatid
from seat_allocation_rule sar
order by ((sar.origin = :origin) << 2) + ((sar.destination = :destination) << 1) + (sar.passenger_type = :passenger_type) desc,
((sar.origin <> '') << 2) + ((sar.destination <> '') << 1) + (sar.passenger_type <> '') asc
limit 1
To understand how:
create table testcase (
origin varchar(255),
destination varchar(255),
passenger_type varchar(255),
expected_seat int(11)
);
insert into testcase values ('Malaysia','Singapore','Senior_Citizen',3),
('Malaysia','Singapore','Student',2),
('Malaysia','US','Student',1);
select * from (
select tc.*,
sar.seatid,
case when sar.seatid = tc.expected_seat then 'Y' else '-' end as pass,
((sar.origin = tc.origin) << 2)
+ ((sar.destination = tc.destination) << 1)
+ ((sar.passenger_type = tc.passenger_type) << 0) as score,
((sar.origin <> '') << 2)
+ ((sar.destination <> '') << 1)
+ ((sar.passenger_type <> '') << 0) as priority
from seat_allocation_rule sar
cross join testcase tc
) x order by expected_seat desc, score desc, priority asc;
This fixes the existing SQL:
SELECT DISTINCT s.SeatID, s.SeatName
FROM SEATS s
LEFT JOIN SEAT_ALLOCATION_RULE r ON r.SeatID = s.SeatID
AND r.Origin = 'Malaysia'
AND (
(r.Destination = 'Singapore' AND r.Passenger_Type IN ('Student', ''))
OR
(r.Destination = '' AND r.Passenger_Type = '')
)
WHERE r.SeatID IS NOT NULL
But it's only a partial solution, and it's hand-coding logic you really want to apply based solely on the data.
A complete solution will use hypothetical inputs for your passenger's ticket info to produce all eligible seats. This is a great use of lateral joins/apply, which are sadly lacking in MySql (all of their major competitors have had these for at least two release cycles, along with other gems that are absent from the current MySql release like windowing functions, ctes, full joins... I could go on). Here's how I'd do it in Sql Server:
SELECT p.PassengerID, s.SeatID, s.SeatName
FROM Passenger p
CROSS APPLY (
SELECT TOP 1 r.SeatID
FROM SEAT_ALLOCATION_RULE r
WHERE COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
ORDER BY
CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END DESC
) r
INNER JOIN SEATS s ON s.SeatID = r.SeatID
WHERE p.PassengerID = /* passenger criteria here */
I know the Sql Server solution isn't much immediate help to you, but perhaps it will suggest a better MySql solution.
Without APPLY, the only way I know to do this is to first compute the MAX() match count for your passengers (how many parts of the rules match):
SELECT p.PassengerID,
MAX(CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END) AS MatchCount
FROM Passenger p
INNER JOIN SEAT_ALLOCATION_RULE r ON COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
GROUP BY p.PassengerID
And then use that to filter down to results that have the same number of matches:
SELECT p
FROM Passenger p
INNER JOIN ( /* matchecounts */
SELECT p.PassengerID,
MAX(CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END) AS MatchCount
FROM Passenger p
INNER JOIN SEAT_ALLOCATION_RULE r ON COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
GROUP BY p.PassengerID
) m ON m.PassengerID = p.PassengerID
INNER JOIN SEAT_ALLOCATION_RULE r ON COALESCE(NULLIF(r.Origin, ''),p.Origin) = p.Origin
AND COALESCE(NULLIF(r.Destination,''), p.Destination) = p.Destination
AND COALESCE(NULLIF(r.Passenger_Type,''),p.Passenger_Type) = p.Passenger_Type
INNER JOIN SEATS s ON s.SeatID = r.SeatID
WHERE m.MatchCount =
(CASE WHEN r.Origin <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Destination <> '' THEN 1 ELSE 0 END
+ CASE WHEN r.Passenger_Type <> '' THEN 1 ELSE 0 END)
AND p.PassengerID = /* Passenger criteria here */
Which repeats a lot of code as well as effort in the DB, and is not very efficient. You can repeat the passenger criteria in the nested query, but that would only help a little. This option might also return multiple records for a passenger if they match two rules equally, though you can solve this easily enough with a GROUP BY expression.
In either case, note you can improve performance and simplify code by using actual NULL values instead of empty strings for missing parts of the SEAT_ALLOCATION_RULE table.

Stored Procedure returns Empty result set on Optional paramaters

When I pass all the values required in query in return correct results. But when I only pass the ID, I get an empty result set. Shouldn't it be ignore the type in that case and still return the correct values?
Can you help me out with this and point out the issues in the query.
SELECT 'elementary_school' AS type, elementary_school AS obj, COUNT(*) AS count
FROM data
WHERE data.report_id = ReportId
AND (data.book_section = type
OR data.book_section IS NULL
OR data.book_section = ''
)
GROUP BY elementary_school
UNION
SELECT 'middle_school' AS type, middle_school AS obj, COUNT(*) AS count
FROM data
WHERE data.report_id = ReportId
AND (data.book_section = type
OR data.book_section IS NULL
OR data.book_section = ''
)
GROUP BY middle_school
UNION
SELECT 'high_school' AS type, high_school AS obj, COUNT(*) AS count
FROM data
WHERE data.report_id = ReportId
AND (data.book_section = type
OR data.book_section IS NULL
OR data.book_section = ''
)
GROUP BY high_school
UNION
SELECT 'lot_sqft' AS type, lot_sqft AS obj, COUNT(*) AS count
FROM data
WHERE data.report_id = ReportId
AND (data.book_section = type
OR data.book_section IS NULL
OR data.book_section = ''
)
GROUP BY lot_sqft
ORDER BY count, type
You can simplify the query:
SELECT t.type,
(CASE WHEN t.type = 'elementary_school' THEN elementary_school
WHEN t.type = 'middle_school' THEN middle_school
WHEN t.type = 'high_school' THEN high_school
WHEN t.type = 'lot_sqft' THEN lot_sqft
END) AS obj, COUNT(*) AS count
FROM data d CROSS JOIN
(SELECT 'elementary_school' AS type UNION ALL
SELECT 'middle_school' AS type UNION ALL
SELECT 'high_school' AS type UNION ALL
SELECT 'lot_sqft' AS type
) t
WHERE d.report_id = #ReportId AND
(d.book_section = #type OR
d.book_section IS NULL OR
d.book_section = ''
)
GROUP BY t.type. obj;
I have highlighted what seem to be "parameters" using # to clarify that they are parameters and not columns in tables.
If you want to check for a NULL value, then change the logic to:
WHERE (d.report_id = #ReportId OR #ReportId IS NULL) AND
(d.book_section = #type OR
d.book_section IS NULL OR
d.book_section = ''
)
Well, It was a silly but honest mistake. I was able to figure it out myself. The problem was I was checking if the column was null or empty but not checking on the value I passed. So it would be something like as follows:
AND (data.book_section = type
OR data.book_section IS NULL
OR data.book_section = ''
)
Converted To:
AND (data.book_section = type
OR type IS NULL
OR type = ''
)
As I only want to check if it was the 'type' which I'm passing a param is empty or null. Anyways way thanks for everyone who tried to help. Cheers.