Set value of MySQL field when displaying data - mysql

I am trying to clean up some MySQL code another programmer put together so that, whenever the state abbreviation is either QC (for Quebec) or ON (for Ontario), the sortOrder is set to 'ZZZZ.'
The MySQL query is pulling from a table with addresses of businesses, and the state field holds the abbreviation of the state or province, and the state_full field holds the full name of the state or province.
This is how the MySQL query looks at the moment:
SELECT DISTINCT
state_full,
state as sortOrder,
'' as state,
state as searchState
FROM vendorlocator
WHERE LENGTH(zip) < 6 OR ((LENGTH(zip) > 5) AND LOCATE('-',zip) > 0)
UNION
(SELECT DISTINCT
state_full,
'ZZZZ',
state,
state as searchState
FROM vendorlocator
WHERE LENGTH(zip) > 5 AND LOCATE('-', zip) = 0)
ORDER BY sortOrder, state
The way the other programmer set this up looks rather complex and messy, in my opinion..is there an easier way to set the value of sortOrder to 'ZZZZ' for addresses located in Quebec (QC) or Ontario (ON)?

You can use case expression in the select clause and >= instead of separate > and = in the where clause:
select distinct state_full
, state as sortOrder
, case when (length(zip) > 5 and locate('-',zip) = 0) then 'ZZZZ' else '' as state
, state as searchState
from vendorlocator
where length(zip) < 6 or ((length(zip) > 5) and locate('-',zip) >= 0)

Related

sql rank row results

I"m trying to add a new col that shows the rank (or sequence) of row results by date.
I've written:
SELECT
#row_number:=(CASE
WHEN #member_id = lh.member_id and lc.ladder_advocacy is not null
THEN #row_number + 1
when #member_id = lh.member_id and lc.ladder_advocacy is null then "null"
ELSE 1 /* there is an error here - i need it to return a 1 if not null, then 2 for the 2nd instance, etc */
END) AS rank_advocacy,
#member_id:=lh.member_id AS member_id,
lh.ladder_change,
lc.name,
lc.ladder_advocacy,
lc.ladder_elected,
lc.ladder_policy,
lc.ladder_engagement,
lc.ladder_newventure,
lc.ladder_collective,
lc.is_trigger
FROM
leenk_ladder_history AS lh
LEFT JOIN
leeds_so.leenk_ladder_config AS lc ON lh.ladder_config_id = lc.id
WHERE
ladder_change = 1 AND trigger_active = 1
ORDER BY member_id, trigger_event_date DESC;
There is an error at row 4, and I'm not sure how to fix it. For the first result, I want to return 1. for the second results, I want to return #row_number + 1. Third result, #row_number+2 (etc).
How do I achieve this?
I don't understand how the condition lc.ladder_advocacy is not null is being used. However, the basic structure is:
SELECT (#row_number = IF(#member_id = lh.member_id, #row_number + 1
IF(#member_id := lh.member_id, 1, 1)
)
) as rank_advocacy,
lh.ladder_change,
. . .
Some really important points:
You need to assign #member_id and #row_number in the same expression. MySQL (as with all other databases) does not guarantee the order of evaluation of expressions.
In more recent versions of MySQL, I think the ORDER BY needs to go in a subquery, with the variable expressions in the outer query.

mysql variable with #variables for calculated values

I have a mysql query where I need to calculate values like ROUND(SUM(temp.total_pq),2) multiple times, so I defined variables to avoid repeating them.
But the line 5 in the query returns wrong value in the results. The value for #diff_client_partner_qtty := ROUND((#partner_qtty_all_runs - #client_qtty_all_runs), 2) AS diff_client_partner_qtty is always NULL the first time I run and thereafter always 84.
I asked the in-house DBA and he says I should not use variables in my query like this because the order in which mysql will set values for the variable is not predictable and hence I may get NULL value.
But why? Also can someone please propose then another way whereby I can avoid rewriting ROUND(SUM(temp.total_pq),2) multiple times other than a subquery. I would prefer to avoid a subquery because I think even in its current form query is not that readable.
SELECT temp.dtaccounted AS accounting_period,
#partner_qtty_all_runs := ROUND(SUM(temp.total_pq),2) AS partner_qtty_all_runs,
ROUND(temp.mmq,2) AS mopay_qtty,
#client_qtty_all_runs := ROUND(SUM(temp.total_cq),2) AS client_qtty_all_runs,
#diff_client_partner_qtty := ROUND((#partner_qtty_all_runs - #client_qtty_all_runs), 2) AS diff_client_partner_qtty,
#partner_gtv := ROUND(temp.total_pq_gtv, 2) AS partner_gtv,
#client_gtv := ROUND(temp.total_cq_gtv,2) AS client_gtv,
#diff_client_partner_gtv := ROUND((#partner_gtv - #client_gtv), 2) AS diff_client_partner_gtv,
temp.stariffcurrency AS tariffcurrency,
ROUND(#diff_client_partner_gtv * ffactor, 2) AS diff_client_partner_gtv_eur,
temp.scountry AS country,
temp.spartnererpid AS partner_erp_id,
c.name AS partner_name,
temp.nproducttype AS product,
temp.capping
FROM
(SELECT SUM(npartnerquantity) AS total_pq,
SUM(nmindmaticsquantity) AS mmq,
SUM(nclientquantity) AS total_cq,
SUM(dgrosstariff * npartnerquantity) AS total_pq_gtv,
SUM(dgrosstariff * nclientquantity) AS total_cq_gtv,
nrun,
vb.scountry,
vb.spartnererpid,
dtaccounted,
stariffcurrency,
vb.nproducttype,
cq.bisenabled AS capping
FROM report_table vb,
client_table cq
WHERE vb.accperiod > '2013-12-01'
AND vb.partnerid = cq.partnerid
AND vb.scountry = cq.scountry
AND vb.nproducttype = cq.nproducttype
AND (cq.dtvalidto IS NULL
OR cq.dtvalidto > vb.accperiod)
GROUP BY scountry,
nproducttype,
partnerid,
nrun,
accperiod
) temp,
customer c,
currency_conversion cc
WHERE temp.partnerid = c.erp_id
AND temp.total_pq <> temp.total_cq
AND cc.scurrencyfrom = temp.stariffcurrency
AND cc.scurrencyto = 'EUR'
AND cc.dtrefdate = temp.accperiod
GROUP BY temp.scountry,
temp.partnerid,
c.name,
temp.nproducttype,
temp.accperiod
ORDER BY temp.accperiod,
temp.scountry,
temp.partnerid,
temp.nproducttype,
temp.capping \G;

How to update duplicated rows with a index (Mysql)

I have a table, named city.
city_name
---------------
New York
Beijing
New York
New York
Dubai
Beijing
---------------
After update, I want it be:
city_name, index
---------------
New York, 0
Beijing, 0
New York, 1
New York, 2
Dubai, 0
Beijing, 1
---------------
The pattern is like this: the first New York will be have an index of 0, the second New York's index is 1 and the third one will be 2. There are millions of rows in this table.
Any easy way to make this update?
I am thinking to solve this problem in two step.
First step:
#cities = Select distinct city_name from city;
Second step:
foreach #cities as #city
update city set index = row_num where city_name = #city.cityname
It seems row_num is not availbe in mysql.
Try this:
update city cross join
(select #city := '', #prevcity := '', #i := 0) const
set `index` = (case when (#prevcity := #city) is null then null
when (#city := city) is null then null
else #i := if(#prevcity = city, #i + 1, 1) is null then null
end)
order by city;
If you are familiar with the use of variables for enumeration in a select statement, then this is similar. The complication is ensuring the order of evaluation for the update. This is handled by using a case statement, which sequentially evaluates each clause until one is true. The first two are guaranteed to be false (because the values should never be NULL).
EDIT:
If you have a unique id, then the solution is a bit easier. I wish you could do this:
update city c
set `index` = (select count(*) from city c2 where c2.city = c.city and c2.id <= c.id);
But instead, you can do it with more joins:
update city c join
(select id, (select count(*) from city c2 where c2.city = c1.city and c2.id <= c1.id) as newind
from city c1
) ci
on c.id = ci.id
set c.`index` = ci.newind;
A way to do this using session() for storage AND comparing purposes:
session_start();
$number=0;
$result=mysqli_query($yourconnection,"SELECT * FROM city ORDER BY city_name");
while($row=mysqli_fetch_array($result)){
if(empty($_SESSION["storage"])){
/* THIS CONDITION ONLY GOES THROUGH THE VERY FIRST ARRAY FETCH */
$_SESSION["storage"]=$row['city_name'];
mysqli_query($yourconnection, "UPDATE city SET index='$number' WHERE city_name='$cityname'"); /* STORE TO THE FIRST CITY'S INDEX 0 */
}
else if($_SESSION["storage"]==$row['city_name']){
/* IF SESSION IS THE SAME COUNTRY AS THE CURRENT ROW COUNTRY */
$_SESSION["storage"]=$row['city_name'];
$number=$number+1; /* INCREMENT NUMBER FOR THE SAME COUNTRY */
mysqli_query($yourconnection, "UPDATE city SET index='$number' WHERE city_name='".$row['city_name']."'");
}
else {
/* THIS IS FOR THE NEXT NEW COUNTRY */
$number=0; /* START AGAIN THE COUNT TO 0 IF NEW COUNTRY */
$_SESSION["storage"]=$row['city_name'];
mysqli_query($yourconnection, "UPDATE city SET index='$number' WHERE city_name='".$row['city_name']."'");
}
} /* END OF WHILE LOOP */
I've done this before, but with different output but with the same logic. AND I use another table for storage purposes and comparing purposes. But the code above that I've made, I used session instead.

SQL Server: issues with field length control

Were are facing a big problem with string length control in SQL Server 2008.
A brief recap of our system:
import data in a persistent staging area from *.txt file (semicolon as separator), using bulk insert in SQL Server environment;
in PSA table all columns are varchar(MAX);
cleaning operations using insert statement based on a select with multiple where conditions.
The problem we deal with is on a single column type and length, in fact in data warehouse level it has to be numeric and its lengths must not exceed 13 digits.
The select is the following:
select cast(LTRIM(RTRIM(data_giacenza)) as numeric),
LTRIM(RTRIM(codice_socio)),
LTRIM(RTRIM(codice_gln)),
LTRIM(RTRIM(tipo_gln)),
LTRIM(RTRIM(codice_articolo_socio)),
LTRIM(RTRIM(codice_ean_prodotto)),
LTRIM(RTRIM(codice_ecat_prodotto)),
LTRIM(RTRIM(famiglia)),
LTRIM(RTRIM(marca)),
LTRIM(RTRIM(classificazione_liv_1)),
LTRIM(RTRIM(classificazione_liv_2)),
LTRIM(RTRIM(classificazione_liv_3)),
LTRIM(RTRIM(classificazione_liv_4)),
LTRIM(RTRIM(modello)),
LTRIM(RTRIM(descrizione_articolo)),
cast(LTRIM(RTRIM(giacenza)) as numeric),
cast(LTRIM(RTRIM(acquistato)) as numeric), 'X' FROM psa_stock a
where EXISTS
(
SELECT 0
FROM(
SELECT
data_giacenza
,codice_socio
,codice_gln
,codice_articolo_socio
FROM psa_stock
where
LEN(LTRIM(RTRIM(data_giacenza))) = 8 and LEN(LTRIM(RTRIM(codice_socio))) = 3
and LEN(LTRIM(RTRIM(codice_gln))) = 13 and LEN(LTRIM(RTRIM(tipo_gln))) = 3
and LEN(LTRIM(RTRIM(codice_articolo_socio))) <= 15
and (LEN(LTRIM(RTRIM(codice_ean_prodotto))) <= 13 or LEN(ISNULL(codice_ean_prodotto, '')) = 0)
and (LEN(LTRIM(RTRIM(codice_ecat_prodotto))) = 9 or LEN(ISNULL(codice_ecat_prodotto, '')) = 0)
and LEN(LTRIM(RTRIM(famiglia))) = 2
and (LEN(LTRIM(RTRIM(marca))) <= 20 or LEN(ISNULL(marca, '')) = 0)
and (LEN(LTRIM(RTRIM(modello))) <= 30 or LEN(ISNULL(modello, '')) = 0)
and (LEN(LTRIM(RTRIM(descrizione_articolo))) <= 50 or LEN(ISNULL(descrizione_articolo, '')) = 0)
and LEN(LTRIM(RTRIM(giacenza))) <= 5
and LEN(LTRIM(RTRIM(acquistato))) <= 5
and (LEN(LTRIM(RTRIM(classificazione_liv_1))) <= 15 or LEN(ISNULL(classificazione_liv_1, '')) = 0)
and (LEN(LTRIM(RTRIM(classificazione_liv_2))) <= 15 or LEN(ISNULL(classificazione_liv_2, '')) = 0)
and (LEN(LTRIM(RTRIM(classificazione_liv_3))) <= 15 or LEN(ISNULL(classificazione_liv_3, '')) = 0)
and (LEN(LTRIM(RTRIM(classificazione_liv_4))) <= 15 or LEN(ISNULL(classificazione_liv_4, '')) = 0)
and ISNUMERIC(ltrim(rtrim(REPLACE(data_giacenza, ' ', '')))) = 1
and ISNUMERIC(ltrim(rtrim(REPLACE(codice_gln, ' ', '')))) = 1
and ISNUMERIC(LTRIM(RTRIM(REPLACE(giacenza, ' ', '')))) = 1 and charindex(',', giacenza) = 0
and ISNUMERIC(LTRIM(RTRIM(REPLACE(acquistato, ' ', '')))) = 1
and ISNUMERIC(ltrim(rtrim(REPLACE(codice_ean_prodotto, ' ', '')))) = 1
and ISNUMERIC(ltrim(rtrim(REPLACE(codice_ecat_prodotto, ' ', '')))) = 1
and codice_socio in (select codice_socio from ana_socio)
and tipo_gln in (select tipo from ana_gln)
and codice_gln in (select codice_gln from dw_key_gln)
group by
data_giacenza
,codice_socio
,codice_gln
,codice_articolo_socio
having COUNT (*) = 1
) b
where
a.data_giacenza = b.data_giacenza and
a.codice_articolo_socio = b.codice_articolo_socio and
a.codice_socio = b.codice_socio and
a.codice_gln = b.codice_gln)
The critical field is codice_ean_prodotto.
In fact, it allows to consider also values as SEAGAT7636490026751,NE20000003039,NE20000002168 which are not numeric and, the first, overlap maximum dimensions.
As result, the insert statement gives back
String o binary data would be truncated
error and fails the insertion.
Thanks in advance! I look forward your help!!!
Enrico
Have you tried executing that query, and adding codice_ean_prodotto = 'NE20000003039' to the where clause? Be sure that these are the actual field that is giving you the problem. If the select returns a row with that where clause, then something's wrong with the logic.
I'm leaning towards your having COUNT (*) = 1 clause in the EXISTS subquery - is it possible to have more than one record for these specific keys? As long as your PK is made up of those 4 fields (data_giacenza, codice_articolo_socio, codice_socio, codice_gln), you shouldn't need the GROUP BY and HAVING clauses at all. If you're not joining on your primary key, it could be that it is the culprit.
It's hard to tell without seeing your data model, however.
I figured out what was wrong.
In the inner select, we were excluding from the selection all records not respecting format constraints and duplication (the meaning of count(*)=1), extracting only the PK of the destination table.
But when selecting with PK we retrieve also those record that were duplicates, but were excluded by the format constraint, leading the insert to error due to dimension issues.
Now I divided the steps:
Duplicates lookup and deletion
Selection with format constraints
It works!

MySQL CASE "Else Case When" is executing when the precondition is true - what am I missing?

I have a table that, due to the third party system we are using, sometimes has duplicate data. Since the model uses an EAV method there's no way to filter this the "right" way, so I am aggregating the data into a View - I know this is a data collection problem but it's easier for me to fix it on the display end than go through this system and potentially break existing data and forms. I need to check one of two fields to see if one or both are entered, but only pick one (otherwise the name displays twice like this: "John,John" instead of just "John"). Here's my code for the relevant part:
group_concat(
(
case when (`s`.`fieldid` = 2) then `s`.`data`
else
case when (`s`.`fieldid` = 35) then `s`.`data`
else NULL end
end
) separator ','),_utf8'') as first_name
If both fieldid 2 and fieldid 35 are entered, I would expect this to just return the value from fieldid = 2 and not the value from fieldid = 35, since the Else clause shouldn't execute when the original case when is true. However it's grabbing that, and then still executing the case when inside of the else clause?
How can I fix this code to give me either fieldid = 2 OR fieldid = 35, but avoid globbing them both together which results in the name being duplicated?
EDIT
Here is the table structure:
table: subscribers_data
subscriberid (int) fieldid (int) data (text)
It uses an E-A-V structure so a sample record might be:
subscriberid fieldid data
1 2 John
1 3 Smith
1 35 John
1 36 Smith
with fieldid 2 and 35 being the custom field "First Name" (defined in a separate table) and fieldid 3 and 36 being "Last Name".
Here is the full view that I'm using:
select `ls`.`subscriberid` AS `id`,
left(`l`.`name`,(locate(_utf8'_',`l`.`name`) - 1)) AS `user_id`,
ifnull(group_concat((
case when (`s`.`fieldid` = 2) then `s`.`data`
when (`s`.`fieldid` = 35) then `s`.`data`
else NULL end) separator ','),_utf8'') AS `first_name`,
ifnull(group_concat((
case when (`s`.`fieldid` = 3) then `s`.`data`
when (`s`.`fieldid` = 36) then `s`.`data`
else NULL end) separator ','),_utf8'') AS `last_name`,
ifnull(`ls`.`emailaddress`,_utf8'') AS `email_address`,
ifnull(group_concat((
case when (`s`.`fieldid` = 81) then `s`.`data`
else NULL end) separator ','),_utf8'') AS `mobile_phone`,
ifnull(group_concat((
case when (`s`.`fieldid` = 100) then `s`.`data`
else NULL end) separator ','),_utf8'') AS `sms_only`
from ((`list_subscribers` `ls`
join `lists` `l` on((`ls`.`listid` = `l`.`listid`)))
left join `subscribers_data` `s` on((`ls`.`subscriberid` = `s`.`subscriberid`)))
where (left(`l`.`name`,(locate(_utf8'_',`l`.`name`) - 1)) regexp _utf8'[[:digit:]]+')
group by `ls`.`subscriberid`,`l`.`name`,`ls`.`emailaddress`
The view is being used as the Model for a Ruby on Rails application, so I'm using some creative hacking to fake out a "user_id" that Rails expects (we name the field list.name in the Lists table using a numeric ID that our front-end Rails app generates when we add a new user, so I'm extracting just this number to make the view look like a Rails-convention database table)
I am not a mysql guy, but in a sql server case statement, you could do it without the first 'else'
case
when fieldid = 2 then data
when fieldid = 35 then data
else null
end
Also, you seem to be returning the same 'data' field in both cases
Anything inside group_concat() doesn't have a way to see the context in which it's running. So, you have have two rows in a single group, one with fieldid=2 and second with fieldid=35, it will do the following:
processing row with fieldid=2...
s.fieldid = 2 is true, return s.data
processing row with fieldid=35...
s.fieldid = 2 is false, try the else part
s.fieldid = 35 is true, return s.data
This explains why is "John" returned multiple times. The only way to fix it is to run a different query outside of group_concat().
EDIT:
Ih you really have to do it this way, use something like this instead:
SELECT ...
min(CASE WHEN s.fieldid IN (2,35) THEN s.data ELSE NULL END) AS first_name
...
Alternatively you can do group_concat(DISTINCT ...) if the two values can't be different (otherwise you would get e.g. "John,Johny"). Why do you have two values for first_name/last_name though?