When I attempt to cast my FLOATS into CHARS in this procedure, I get null values in the database. Location is a Geospatial field. What am I doing wrong?
CREATE DEFINER=`me`#`%` PROCEDURE `UpdateLocationByObjectId`(IN objectId INT,
IN latitude FLOAT,
IN longitude FLOAT)
BEGIN
UPDATE Positions P
JOIN Objects O ON P.Id = O.PositionId
SET P.Location = GeomFromText('Point(' + CAST(latitude AS CHAR(10)) + ' ' + CAST(longitude AS CHAR(10)) +')')
WHERE O.ObjectId = objectId;
END
If I use this as a test, it works fine.
CREATE DEFINER=`me`#`%` PROCEDURE `UpdateLocationByObjectId`(IN objectId INT,
IN latitude FLOAT,
IN longitude FLOAT)
BEGIN
UPDATE Positions P
JOIN Objects O ON P.Id = O.PositionId
SET P.Location = GeomFromText('Point(10 10')')
WHERE O.ObjectId = objectId;
END
Change this line
SET P.Location = GeomFromText('Point(' + CAST(latitude AS CHAR(10)) + ' '
+ CAST(longitude AS CHAR(10)) +')')
To
SET P.Location = GeomFromText(concat('Point(' , CAST(latitude AS CHAR(10)) , ' '
, CAST(longitude AS CHAR(10)) ,')'))
The + operator is adding your text values ('10' + '10') = 20
So the center part evaluates to 'Point(' + 20 + ')', adding text that cannot be read as number + numbers evaluates to NULL.
Only the concat function can concatenate strings.
In fact this code will work just as well:
SET P.Location = GeomFromText(concat('Point(', latitude, ' ', longitude,')'))
Related
I've a library of questions and answers and building an API in NodeJS which allows to search for answers based on the question passed as input. Following is my goal:
Split the question by space
Tokenize it and remove stopwords
Query database for records where question contains one or more words from the tokenized array
Ideally sort in descending order total number of matches in the question. For eg: If the question A contains 'module' and 'solution' and question B contains only 'solution', then question A should be shown before question B
I've been able to achieve 1 to 3, using the below code:
let question = req.query.question;
let arrQuestions = question.split(" ");
let tokenizedQuestion = stopwords.removeStopwords(arrQuestions);
let whereClause = tokenizedQuestion.join("%' OR answer LIKE '%");
whereClause = " answer LIKE '%" + whereClause + "%' ";
let query = "SELECT * FROM tbl_libraries WHERE " + whereClause;
I'm not able to figure out how to achieve 4. Can somebody provide pointers?
Thanks!
Are you sure that you do not want to use MySQL fulltext search for this?
If the answer is some flavour of 'No', you can continue reading...
In one of my project I was implementing something like this.
Query wise it looks like this (simplified version):
SELECT
name
FROM
table
WHERE
name REGEXP 'term1|term2|term3' -- you can use your OR + LIKE way
ORDER BY
SP_TermsWeitght(name, 'term1 term2 term3') DESC
All the magic is in my SP_TermsWieght function that returns "weight" (number) and I'm supplying a list of terms (cleaned and normalized) to the function.
The function:
CREATE FUNCTION `SP_TermsWeight`(
`sValue` TEXT,
`sTerms` VARCHAR(127)
)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE p INT DEFAULT 1;
DECLARE w INT DEFAULT 0;
DECLARE l INT;
DECLARE c CHAR(1);
DECLARE s VARCHAR(63);
DECLARE delimiters VARCHAR(15) DEFAULT ' ,';
SET sTerms = TRIM(sTerms);
SET l = LENGTH(sTerms);
IF (l > 0) THEN
-- checking is value matched terms exactly
IF (sTerms = sValue) THEN
SET w = 50000;
ELSE
-- supposing that "the terms" is one single term so it it match in full, the weight will be high
IF (l <= 63) THEN
SET w = w + SP_TermWeight(sValue, sTerms, 5000, 1000, 100);
END IF;
-- not processing it term by term if it is already matched as full
IF (w = 0) THEN
-- processing term by term using space or comma as delimiter
WHILE i <= l DO
BEGIN
SET c = SUBSTRING(sTerms, i, 1);
IF (LOCATE(c, delimiters) > 0) THEN
SET s = SUBSTRING(sTerms, p, i - p);
SET w = w + SP_TermWeight(sValue, s, 50, 10, 0);
SET p = i + 1;
END IF;
SET i = i + 1;
END;
END WHILE;
IF (p > 1 AND p < i) THEN
SET s = SUBSTRING(sTerms, p, i - 1);
SET w = w + SP_TermWeight(sValue, s, 50, 10, 0);
END IF;
END IF;
END IF;
END IF;
RETURN w;
END
Technically speaking it is 'separating' terms using delimiters and checking if the value "contains" the term.
It's a bit hard to explain everything what it does (I've added a few comments in the code for you).
Feel free to ask questions if you do not understand some bits.
In your case it can be simplified dramatically as you do not need to differentiate begin/end/middle matches.
Another helper function that used internally:
CREATE FUNCTION `SP_TermWeight`(
`sValue` TEXT,
`sTerm` VARCHAR(63),
`iWeightBegin` INT,
`iWeightEnd` INT,
`iWeightMiddle` INT
)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE r INT DEFAULT 0;
SET sTerm = TRIM(sTerm);
IF (LENGTH(sTerm) > 1) THEN
IF (iWeightBegin != 0 AND sValue REGEXP CONCAT('[[:<:]]', sTerm)) THEN
SET r = r + iWeightBegin;
END IF;
IF (iWeightEnd != 0 AND sValue REGEXP CONCAT(sTerm, '[[:>:]]')) THEN
SET r = r + iWeightEnd;
END IF;
IF (r = 0 AND iWeightMiddle != 0 AND sValue REGEXP sTerm) THEN
SET r = r + iWeightMiddle;
END IF;
END IF;
RETURN r;
END
This function used for assigning different weights if the term matched to value from the beginning of the string, at the end of the string or in the middle. It is important in my case. In your case it might be simple LIKE.
I ended up using Full Text Search. Following is the stored procedure I created to enable searching:
DROP PROCEDURE IF EXISTS SP_Search $$
CREATE PROCEDURE `SP_Search`(IN QuestionToSearch TEXT, IN TagsToSearch TEXT, IN CollectionsToSearch TEXT, IN ReturnRecordsFromIndex INT, IN TotalRecordsToReturn INT)
BEGIN
SET #MainQuery = CONCAT("SELECT *, MATCH(question, answer_content) AGAINST (", CONCAT("'", QuestionToSearch, "'"), " IN NATURAL LANGUAGE MODE) AS score ");
SET #MainQuery = CONCAT(#MainQuery, " FROM tbl_libraries ");
SET #MainQuery = CONCAT(#MainQuery, " WHERE MATCH(question, answer_content) AGAINST (", CONCAT("'", QuestionToSearch, "'"), " IN NATURAL LANGUAGE MODE) ");
IF F_IsNullOrEmpty(TagsToSearch) AND NOT F_IsNullOrEmpty(CollectionsToSearch) THEN
SET #MainQuery = CONCAT(#MainQuery, " AND collections LIKE '%", CollectionsToSearch, "%' ");
ELSEIF F_IsNullOrEmpty(CollectionsToSearch) AND NOT F_IsNullOrEmpty(TagsToSearch) THEN
SET #MainQuery = CONCAT(#MainQuery, " AND tags LIKE '", TagsToSearch, "' ");
ELSEIF NOT F_IsNullOrEmpty(TagsToSearch) AND NOT F_IsNullOrEmpty(CollectionsToSearch) THEN
SET #MainQuery = CONCAT(#MainQuery, " AND tags LIKE '", TagsToSearch, "' AND collections LIKE '", CollectionsToSearch, "' ");
END IF;
SET #MainQuery = CONCAT(#MainQuery, " ORDER BY score DESC ");
SET #MainQuery = CONCAT(#MainQuery, " LIMIT ", ReturnRecordsFromIndex, ", ", TotalRecordsToReturn);
PREPARE SqlQuery FROM #MainQuery;
EXECUTE SqlQuery;
END $$
DELIMITER ;
This uses a custom function I created F_IsNullOrEmpty, which is as shown below for completion:
CREATE FUNCTION F_IsNullOrEmpty(ValueToCheck VARCHAR(256)) RETURNS BOOL
DETERMINISTIC
BEGIN
IF((ValueToCheck IS NULL) OR (LENGTH(ValueToCheck) = 0) OR (ValueToCheck = 'null')) THEN
Return True;
ELSE
Return False;
END IF;
END;
I am trying to generate a tool tip text using SQL. The text generated is passed as Title Attribute in HTML. There needs to be some newline characters generated in the tool tip.
I have used the following -
CHAR(13); CHAR(10); <br>; \n.
However in all cases, I see the character itself in HTML and not a new line.
Any idea how to achieve this?
the SQL is something like this
(SELECT
STUFF(';' + WOR.OrderNo + ' - ' + P.ProductNo + ' - ' + CAST(CAST(ROUND(WOR.OrderQuantity , 0) as int) as varchar(20)) + '; <br/> ', 1, 1, '')
FROM
[ORDER] WOR
JOIN
PRODUCT P
ON P.ID = WOR.ProductID
JOIN
PRODUCT_GROUP PGR
ON P.ID = PGR.ProductID FOR XML PATH(''),TYPE).value('.','nvarchar(MAX)')```
And the Tootip that I see is the following
```SMU_100000021 - A-WHEL-001 - 100;<br/>SMU_100000023 - A-WHEL-001 - 90;<br/>```
The CHAR(10) did the trick.
(SELECT
STUFF(';' + WOR.OrderNo + ' - ' + P.ProductNo + ' - ' + CAST(CAST(ROUND(WOR.OrderQuantity , 0) as int) as varchar(20)) +' '+ CHAR(10) + ' ', 1, 1, '')
FROM
[ORDER] WOR
JOIN
PRODUCT P
ON P.ID = WOR.ProductID
JOIN
PRODUCT_GROUP PGR
ON P.ID = PGR.ProductID FOR XML PATH(''),TYPE).value('.','nvarchar(MAX)')
declare #qry nvarchar(max)
set #qry='IType, INum, IDate, PO, FCode, Tx, Fr, TI, Not'
select #qry = 'select distinct ti.ID,' + #qry +
' from tblInfo ti inner join tblheadr th on ti.IA=1 AND ti.BId = ' +
CAST(#BId as varchar) + ' AND th.CUId =' + CAST(#UserID as varchar)
Now I want my query as
select distinct
ti.ID, ti.IType, ti.INum, ti.IDate, ti.PO, ti.FCode, ti.Tx, ti.Fr, ti.TI, ti.Not
from
tblInfo ti
inner join
tblheadr th on ti.IA = 1 AND ti.BId = 285 and th.CUId = 2
I need to add 'ti.' for each value in #qry..
Can you suggest me how to separate it add ti. in between #qry ?
Here's a shorter solution:
SET #qry = 'IType, INum, IDate, PO, FCode, Tx, Fr, TI, [Not]'
SET #qry = 'ti.' + REPLACE(#qry, ', ', ', ti.')
You should put Not in square brackets cause it's a reserved SQL keyword.
This should work:
select #qry =
'select distinct ti.ID,'
+ replace(replace(replace(#qry, ', Not', ', [Not]'), #qry, 'ti.' + #qry), ', ',', ti.')
+' from tblInfo ti inner join tblheadr th on ti.IA=1 AND ti.BId = '
+ QUOTENAME(CAST(#BId as varchar(max)), '''')
+ ' AND th.CUId ='
+ QUOTENAME(CAST(#UserID as varchar(max)),'''')
You really should specify size for the varchar casts, and also note that notis a keyword that needs to be escaped, so I added that. Most likely you also need to use QUOTENAME to enclose the parameter values in quotes (I added that too).
The resulting#qryvariable will be this:
select distinct
ti.ID,IType, ti.INum, ti.IDate, ti.PO, ti.FCode, ti.Tx, ti.Fr, ti.TI, ti.[Not]
from tblInfo ti
inner join tblheadr th on ti.IA=1 AND ti.BId = 'BidA' AND th.CUId ='UserA'
If the variables are declared as:
declare #userid nvarchar(max) = 'UserA'
declare #bid nvarchar(max) = 'BidA'
On a side note: looking at your join between tblInfo and tblHeadr it seems like there's no join condition, only conditions on the separate tables (that should probably be written in a WHERE clause). Maybe you forgot the join th.? = ti.? ?
How come this geometry point does not intersect with the polygon? I know for a fact that the point exists in the given polygon. Any reason why it returns 0?
DECLARE #point geometry
DECLARE #poly geometry
SET #point = geometry::STGeomFromText('POINT (-79.393967 43.640056)', 4326)
DECLARE #minY varchar(20) = N'-79.37776573850101'
DECLARE #maxY varchar(20) = N'-79.41055306149906'
DECLARE #minX varchar(20) = N'43.63590433545648'
DECLARE #maxX varchar(20) = N'43.64460037532088'
DECLARE #boundingRect varchar(250)
SET #boundingRect = 'POLYGON((' + #minX + ' ' + #minY + ', ' +
#maxX + ' ' + #minY + ', ' +
#maxX + ' ' + #maxY + ', ' +
#minX + ' ' + #maxY + ', ' +
#minX + ' ' + #minY + '))'
SET #poly = geometry::STGeomFromText(#boundingRect, 4326)
SELECT #point.STIntersects(#poly)
I'm not familiar with this SQL notation, so I may be way off base, but I see that your X values seem to be associated with latitude 43N, and Y with longitude 79W. However, your POINT entries might be reversed?
Just a thought: When does a point intersect with a polygon? If it lies INSIDE the polygon? No. Only if it lies directly on one of the edges of the polygon, right?
Try this: Intersect a point with the polygon, which is lying directly on one of the edges of the polygon. If it returns else then 0 you have your answer.
The point should be declared as:
SET #point = geometry::STGeomFromText('POINT (43.640056 -79.393967)', 4326)
Then the Intersect shows the result you're expecting: 1
I have a table, MapLocation, which has a column and two relationships with tables that have a field that really need to be displayed as a single concatenated value. I was thinking this was a perfect case for a computed column, but not sure how to go about it.
MapLocation MaoNo Section
_____________________ _____________________ _____________________
MapNoId MapNoId SectionId
SectionId MapNumber (int) Section (int)
Identifier (nvarchar)
LocationName (nvarchar)
LocationName = "MapNUmber - SectionNumber - Identifier"
ex: 20 - 03 - SW4
How would I write that? I haven't done much with computed columns or concatenating in SQL.
Edit:
I need an actual computed column that is automatically updated, im looking for the formula. Or is this more of a function/trigger? Its possible, I certainly barely know what I'm doing. The idea is that I dont want to have to do two more server calls and concatenate these values client side.
You would use something like this to get the value:
select cast(n.MapNumber as nvarchar(10)) + ' - ' -- cast the MapNumber
+ cast(s.SectionId as nvarchar(10)) + ' - ' -- cast the SectionId
+ l.Identifier
from MapLocation l
left join MaoNo n
on l.MapNoId = n.MapNoId
left join Section s
on l.SectionId = s.SectionId
Then if you need to perform an UPDATE:
update l
set l.LocationName = (cast(n.MapNumber as nvarchar(10)) + ' - '
+ cast(s.SectionId as nvarchar(10)) + ' - '
+ l.Identifier)
from MapLocation l
left join MaoNo n
on l.MapNoId = n.MapNoId
left join Section s
on l.SectionId = s.SectionId
Edit #1 - you can use a TRIGGER:
CREATE TRIGGER trig_LocationName
ON MapLocation
AFTER INSERT
AS
Begin
update MapLocation
set LocationName = (cast(n.MapNumber as nvarchar(10)) + ' - '
+ cast(s.SectionId as nvarchar(10)) + ' - '
+ i.Identifier)
from Inserted i
left join MaoNo n
on i.MapNoId = n.MapNoId
left join Section s
on i.SectionId = s.SectionId
where MapLocation.MapNoId = i.MapNoId -- fields here to make sure you update the correct record
End