Why do my SQL queries not agree with each other? - sql

Hoping this is something I can fix, or maybe someone can point out an obvious mistake.
I have two queries:
SELECT HIDEORSHOW FROM tblProspects WHERE PROSPECT_ID = 1261484;
HIDEORSHOW
1
SELECT PROSPECT_ID FROM tblProspects WHERE HIDEORSHOW = 1;
PROSPECT_ID
196248
199004
204190
204338
210918
211932
213332
214186
216980
218254
222420
223578
223824
224429
224390
224672
224714
227031
227481
228363
230040
238168
239230
240790
241409
243827
244553
245785
247947
248349
250426
250640
252399
252555
253610
253949
254641
255109
Sorry for the long list I just want you guys to see the madness. Is there any reason why this would happen? There is another prospect that I know of, 1257506, that has this HIDEORSHOW value and does not appear in the list.

I would lay good money that the HIDEORSHOW field is a string of some kind, and that some values have leading or trailing spaces.
This is true, due to implicit CASTing: '1' = 1 (Becomes : '1' = '1')
This is false, even with implicit CASTing: ' 1' = 1 (Becomes : ' 1' = '1')
To test this, try this query (or it's equivalent in your version of SQL)...
SELECT PROSPECT_ID FROM tblProspects WHERE CAST(HIDEORSHOW AS INT) = 1;
This will force the casting to be string => int rather than the other way around.
Or you could try this test...
SELECT '<' + HIDEORSHOW + '>', LEN(HIDEORSHOW) FROM tblProspects WHERE PROSPECT_ID = 1261484;
You'll then have more visibility on the exact value in that field.

Related

How to write an Open SQL statement with substring in the JOIN ON condition? [duplicate]

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

Struggling with simple boolean WHERE clause

Tired brain - perhaps you can help.
My table has two bit fields:
1) TestedByPCL and
2) TestedBySPC.
Both may = 1.
The user interface has two corresponding check boxes. In the code I convert the checks to int.
int TestedBySPC = SearchSPC ? 1 : 0;
int TestedByPCL = SearchPCL ? 1 : 0;
My WHERE clause looks something like this:
WHERE TestedByPCL = {TestedByPCL.ToString()} AND TestedBySPC = {TestedBySPC.ToString()}
The problem is when only one checkbox is selected I want to return rows having the corresponding field set to 1 or both fields set to 1.
Now when both fields are set to 1 my WHERE clause requires both check boxes to be checked instead of only one.
So, if one checkbox is ticked return records with with that field = 1 , regardless of whether the other field = 1.
Second attempt (I think I've got it now):
WHERE ((TestedByPCL = {chkTestedByPCL.IsChecked} AND TestedBySPC = {chkTestedBySPC.IsChecked})
OR
(TestedByPCL = 1 AND TestedBySPC = 1 AND 1 IN ({chkTestedByPCL.IsChecked}, {chkTestedBySPC.IsChecked})))
Misunderstood the question.
Change the AND to an OR:
WHERE TestedByPCL = {chkTestedByPCL.IsChecked} OR TestedBySPC = {chkTestedBySPC.IsChecked}
Also:
SQL Server does not have a Boolean data type, it's closest option is a bit data type.
The usage of curly brackets suggests using string concatenations to build your where clause. This might not be a big deal when you're handling checkboxes but it's a security risk when handling free text input as it's an open door for SQL injection attacks. Better use parameters whenever you can.

Calculating total value of Numeric values assigned to string tokens in SQL column

This is kind of complicated, so bear with me.
I've got the basic concept figured out thanks to THIS QUESTION
SELECT LENGTH(col) - LENGTH(REPLACE(col, 'Y', ''))
Very clever solution.
Problem is, I'm trying to count the number of instances of a string token, then take that counter and multiply it by a modifier that represents the string's numeric value. Oh, and I've got a list of 50-ish tokens, each with a different value.
So, take the string "{5}{X}{W}{b/r}{2/u}{pg}"
Looking up the list of tokens and their numeric value, we get this:
{5} 5
{X} 0
{W} 1
{b/r} 1
{2/u} 2
{pg} 1
.... ....
Therefore, the sum value of the string above is 5+0+1+1+2+1 = 10
Now, what I'm really looking for is to a way to do a Join and perform the aforementioned replace-token-get-length trick for each column of the TokenValue table.
Does that make sense?
Psuedo-SQL example:
SELECT StringColumn, TotalTokenValue
???
FROM TableWithString, TokenValueTable
Perhaps this would work better as a custom Function?
EDIT
I think I'm about halfway there, but it's kind of ugly.
SELECT StringColumn, LEN(StringColumn) AS TotalLen, Token,
{ fn LENGTH(Token) } AS TokenLength, TokenValue,
{ fn REPLACE(StringColumn, Token, '') AS Replaced,
{ fn LENGTH(Replaced) } AS RepLen,
{ TotalLen - RepLen / TokenLength} AS TokenCount },
{ TokenCount * TokenValue} CalculatedTokenValue
FROM StringTable CROSS JOIN
TokenTable
Then I need to wrap that in some kind of Group By and get SUM(CalculatedTokenValue)
I can picture it in my head, having trouble getting the SQL to work.
If you create a view like this one :
Create or replace view ColumnsTokens as
select StringColumn, Token, TokenValue,
(length(StringColumn) - length(replace(StringColumn, token, ''))) / length(Token) TokenCount
from StringTable
join TokenTable on replace(StringColumn, Token, '') <> StringColumn ;
that will act as a many-to-many relationship table between columns and tokens, I think you can easily write any query you need. For example,this will give you the total score :
select StringColumn, sum(TokenCount * TokenValue) TotalTokenScore
from ColumnsTokens
group by StringColumn ;
(You're only missing the StringColumns with no tokens)

Regexes: How to use the length of a match in the replacement expression

I'm trying to write a regex that will reformat some boilerplate SQL code into a cleaner version of itself. Here's an example.
I have:
SELECT
[Person].[Name]
[Person].[Address],
[Sales].[TotalAmount]
I want:
SELECT
[Name] = [Person].[Name],
[Address] = [Person].[Address],
[TotalAmount] = [Sales].[TotalAmount]
I can write an expression to move the field name to the left-hand side of the assignment statement, no problem. The tricky part is getting the spacing right – is there some way to use the length of the match in the replacement? And additionally, to use the length of the longest match? So that "[Name]" would be followed by length("TotalAmount") - length("Name") = 7 spaces, "[Address]" would be followed by length("TotalAmount") - length("Address") = 4 spaces, and so on?
I'm not sure this is possible with regular expressions, but I thought I'd throw it out there in the hope that there is some regex guru who knows how to do this.
Off language but still going to post it.
In PHP there is preg_replace_callback() which let you match a string with regex and then process it with PHP. So here's a full PHP solution, note that you need PHP 5.3+:
$sql = 'SELECT
[Person].[Name]
[Person].[Address],
[Sales].[TotalAmount]';
preg_match_all('#\[[^]]+\]\.(\[[^]]+\])#', $sql, $found); // Match
$max = max(array_map('strlen', $found[1])); // Find max length
$new = preg_replace_callback('#\[[^]]+\]\.(\[[^]]+\])#', function($m)use($max){
$string = $m[1].str_repeat(' ',$max - strlen($m[1])).' = '.$m[0]; // Constructing
return($string);
}, $sql);
echo $new;
Output:
SELECT
[Name] = [Person].[Name]
[Address] = [Person].[Address],
[TotalAmount] = [Sales].[TotalAmount]
Online demo

MySQL conditional statement

Alright, so I have a query that looks like this:
SELECT
`orders`.*,
GROUP_CONCAT(
CONCAT(
`menu_items`.`name`,
' ($',
FORMAT(`menu_items`.`price`,2),
')'
) SEPARATOR '<br>'
) as `items`,
SUM(`menu_items`.`price`) as `additional`,
`children`.`first_name`,
`children`.`last_name`,
`organizations`.`base_price`
FROM
`orders`, `order_items`, `menu_items`, `children`, `organizations`
WHERE
`order_items`.`menu_item_id` = `menu_items`.`id` AND
`order_items`.`order_id` = `orders`.`id` AND
`orders`.`added_by` = {$user_id} AND
`orders`.`date` > '{$cutoff}' AND
`children`.`id` = `orders`.`child_id` AND
`organizations`.`id` = `children`.`organization_id`
GROUP BY
`orders`.`id`
I know it's a monstrosity and that some people will die before not using explicit joins. Ignoring that, however, what I wish to do is to only use the CONCAT inside the GROUP_CONCAT if the menu_items.price is greater than 0, otherwise only return menu_items.name. I have had, however, no success trying to throw an IF in there. I've read the manual but all the ways that I've tried aren't working and I'm pretty sure I'm missing something on the whole conditional statements thing.
Have you tried using something like this?
CASE WHEN 'menu_items'.'price' = 0 THEN 'menu.items'.'name' ELSE CONCAT (etc) END
Replacing the CONCAT statement of course.
Something like this should work (but I didn't test it, sorry):
GROUP_CONCAT(
CONCAT(
`menu_items`.`name`,
IF(`menu_items`.`price` > 0, -- <condition>
CONCAT(' ($', FORMAT(`menu_items`.`price`,2), ')'), -- <true-expr>
'' -- <false-expr>
)
)
SEPARATOR '<br>'
) as `items`,
The IF() function is really simple:
IF( <condition>, <true-expr>, <false-expr> )
The function has three arguments: the first is <condition>. If the condition evaluates to true, the function returns the result of <true-expr>. Else the function returns the result of <false-expr>.
Things get harder to get right when you use really long, multi-line expressions that contain parentheses and commas and so on. You just have to do it carefully. I suggest starting with more simple expressions and then build up.