Why selectrow_array does not work with null values in where clause - perl

I am trying to fetch the count from SQL server database and it gives 0 for fields with null values. Below is what I am using.
my $sql = q{SELECT count(*) from customer where first_name = ? and last_name = ?};
my #bind_values = ($first_name, $last_name);
my $count = $dbh->selectrow_array($sql, undef, #bind_values);
This returns 0 if either value is null in the database. I know prepare automatically makes it is null if the passed parameter is undef, but I don't know why it's not working.
So here is weird observation. When I type the SQL with values in Toda for SQL server, it works :
SELECT count(*) from customer where first_name = 'bob' and last_name is null
but when I try the same query and pass values in the parameter for the first_name = bob and the last_name {null} . it does not work.
SELECT count(*) from customer where first_name = ? and last_name = ?

For NULL in the WHERE clause you simply need a different query. I write them below each other, so you can spot the difference:
...("select * from test where col2 = ?", undef, 1);
...("select * from test where col2 is ?", undef, undef);
...("select * from test where col2 is ?", undef, 1);
...("select * from test where col2 = ?", undef, undef);
The first two commands work, stick to those. The third is a syntax error, the fourth is what you tried and which indeed does not return anything.
The DBI manpage has a section of NULL values that talks about this case a bit more.

So, here it is what I did. I added or field is null statement with each field if the value is undef.
my $sql = q{SELECT count(*) from customer where (first_name = ? or (first_name is null and ? = 1)) and (last_name = ? or (last_name is null and ? = 1))};
my #bind_values = ($first_name, defined($first_name)?0:1, $last_name, defined($last_name)?0:1);
my $count = $dbh->selectrow_array($sql, undef, #bind_values);
If anyone has better solution please post it.

Related

Matching records by ID and displaying non-matching columns in TSQL

In SQL Server I have two tables, Registrar Records and Teacher Records which are identical. The first part of the query matches on the student ID and shows whether or not a match was found in a dynamic column (that part I did already).
An additional dynamic column should list out all the columns that didn't match - I'm wondering if there is a way to do this without making a gigantic Case expression since that would have many possibilities, this is what I have so far:
SET ANSI_NULLS OFF
SELECT TR.*,
CASE WHEN RR.StudentID IS NULL THEN 'NO MATCH'
WHEN RR.StudentID IS NOT NULL AND RR.FirstName = TR.FirstName
AND RR.LastName = TR.LastName
AND RR.Floor = TR.Floor
AND RR.FirstQuarterGrade = TR.FirstQuarterGrade
AND RR.SecondQuarterGrade = TR.SecondQuarterGrade
AND RR.ThirdQuarterGrade = TR.ThirdQuarterGrade
AND RR.FinalGrade = TR.FinalGrade THEN 'MATCH'
ELSE 'MATCH WITH ISSUE' END AS MatchResult
--TO DO: Add a ISSUE column; lists columns with mismatch
FROM TeacherRecords TR
LEFT JOIN RegistrarRecords RR ON RR.StudentID = TR.StudentID
If a script is needed for the table here is the table:
CREATE TABLE [dbo].[RegistrarRecords](
[StudentID] [varchar](10) NOT NULL,
[FirstName] [varchar](50) NULL,
[LastName] [varchar](50) NULL,
[Floor] [int] NULL,
[Room] [varchar](10) NULL,
[FirstQuarterGrade] [int] NULL,
[SecondQuarterGrade] [int] NULL,
[ThirdQuarterGrade] [int] NULL,
[FinalGrade] [int] NULL
) ON [PRIMARY]
You could split this query into two parts
First find all the issues
Then check if there are any issues in doing the final select
Here is sample code to do so. The Issue_List field contains a comma-separated list of all the columns with issues (e.g., 'Floor, FirstQuarterGrade'). If this field is empty, then it represents that there are no issues.
WITH TR_Match_Info AS
(SELECT TR.*,
RR.StudentID AS RR_StudentID,
'' + CASE WHEN RR.FirstName = TR.FirstName THEN '' ELSE 'FirstName, ' END
+ CASE WHEN RR.LastName = TR.LastName THEN '' ELSE 'LastName, ' END
+ CASE WHEN RR.Floor = TR.Floor THEN '' ELSE 'Floor, ' END
+ CASE WHEN RR.FirstQuarterGrade = TR.FirstQuarterGrade THEN '' ELSE 'FirstQuarterGrade, ' END
+ CASE WHEN RR.SecondQuarterGrade = TR.SecondQuarterGrade THEN '' ELSE 'SecondQuarterGrade, ' END
+ CASE WHEN RR.ThirdQuarterGrade = TR.ThirdQuarterGrade THEN '' ELSE 'ThirdQuarterGrade, ' END
+ CASE WHEN RR.FinalGrade = TR.FinalGrade THEN '' ELSE 'FinalGrade, ' END
AS Issue_List
FROM TeacherRecords TR
LEFT JOIN RegistrarRecords RR ON RR.StudentID = TR.StudentID
)
SELECT [StudentID],
[FirstName],
[LastName],
[Floor],
[Room],
[FirstQuarterGrade],
[SecondQuarterGrade],
[ThirdQuarterGrade],
[FinalGrade],
CASE WHEN RR_StudentID IS NULL THEN 'NO MATCH'
WHEN Issue_List = '' THEN 'MATCH'
ELSE 'MATCH WITH ISSUE' END
AS MatchResult,
CASE WHEN RR_StudentID IS NULL THEN ''
WHEN LEN(Issue_List) > 0 THEN LEFT(Issue_List, LEN(Issue_List) - 1)
ELSE Issue_List
END AS Match_Issues
FROM TR_Match_Info;
Note that the issue checks above are based on your code. You should review how NULLs are handled - if the values are both NULL it flags it as an issue (e.g., when checking whether NULL = NULL the result is NULL so it goes to the ELSE component of these CASE expressions).

Eloquent how to return null

I have the following query:
SELECT null as id, null as cost, null as title FROM books LIMIT 1
It returns one single record.
Using Eloquent, I've tried
$listagem = Book::where('id','=',-1)->get();// there is no id = -1
$listagem = Livro::select('NULL as id' ,'NULL as cost', 'NULL as title')->get();
(...)
but nothing works.
What is the correct sintax?
Thanks

Interconnecting tables on PostgreSQL

I am a newbie here.
I am using PostgreSQL to manipulate lots of data in my specific field of research. Unfortunately, I am encountering a problem that is not allowing me to continue my analysis. I tried to simplify my problem to clearly illustrate it.
Let's suppose I have a table called "Buyers" with those data:
table_buyers
The buyers can make ONLY ONE purchase in each store or none. There are three stores and there a table for each one. Just like below:
table_store1
table_store2
table_store3
To create the tables, I am using the following code:
CREATE TABLE public.buyer
(
ID integer NOT NULL PRIMARY KEY,
name text NOT NULL,
phone text NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store1
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store2
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store3
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
To add the information on the tables, I am using the following code:
INSERT INTO buyer (ID, name, phone) VALUES
(1, 'Alex', 88888888),
(2, 'Igor', 77777777),
(3, 'Mike', 66666666);
INSERT INTO Store1 (ID_buyer, total_order, total_itens) VALUES
(1, 87.45, 8),
(2, 14.00, 3),
(3, 12.40, 4);
INSERT INTO Store2 (ID_buyer, total_order, total_itens) VALUES
(1, 785.12, 7),
(2, 9874.21, 25);
INSERT INTO Store3 (ID_buyer, total_order, total_itens) VALUES
(2, 45.87, 1);
As all the tables are interconnected by buyer's ID, I wish I could have a query that generates an output just like this:
desired output table.
Please, note that if the buyer did not buy anything in a store, I must print '0'.
I know this is an easy task, but unfortunately, I have been failing on accomplish it.
Using the 'AND' logical operator, I tried the following code to accomplish this task:
SELECT
buyer.id,
buyer.name,
store1.total_order,
store2.total_order,
store3.total_order
FROM
public.buyer,
public.store1,
public.store2,
public.store3
WHERE
buyer.id = store1.id_buyer AND
buyer.id = store2.id_buyer AND
buyer.id = store3.id_buyer;
But, obviously, it just returned 'Igor' as this was the only buyer that have bought items on all three stores (print screen).
Then, I tried the 'OR' logical operator, just like the following code:
SELECT
buyer.id,
buyer.name,
store1.total_order,
store2.total_order,
store3.total_order
FROM
public.buyer,
public.store1,
public.store2,
public.store3
WHERE
buyer.id = store1.id_buyer OR
buyer.id = store2.id_buyer OR
buyer.id = store3.id_buyer;
But then, it returns 12 lines with wrong values (print screen).
Clearly, my mistake is about not considering that 'Buyers' don't have to on all three stores on my code. I just can't correct it on my own, can you please help me?
I appreciate a lot for an answer that can light up my way. Thanks a lot!
Tips about how I can search for this issue are very welcome as well!
Ok. I doubt that this is the final answer for you, but its a start
SELECT
buyer.id,
buyer.name,
COALESCE( gb_store1.total_orders, 0 ) as store1_total,
COALESCE( gb_store2.total_orders, 0 ) as store2_total,
COALESCE( gb_store3.total_orders, 0 ) as store3_total
FROM
public.buyer,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store1
GROUP BY ID_buyer ) gb_store1 ON gb_store1.id_buyer = buyer.id ,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store2
GROUP BY ID_buyer ) gb_store2 ON gb_store2.id_buyer = buyer.id ,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store3
GROUP BY ID_buyer ) gb_store3 ON gb_store3.id_buyer = buyer.id ;
So, this query has a couple elements should focus on. The subselects/groupby allow you to total within your subtables by ID_buyer. The LEFT OUTER JOIN make its so your query can still return a result, even if a subselect finds no matching record. Finally, the COALESCE allows you to return 0 when one of your totals is NULL (because the subselect found no match).
Hope this helps.

field appears null or empty sting but I can not select it

select *
from ss.mailer_data
where
id = 249122
and address_3 like'%%'
will not hit on address_3. I've tried changing the last line to
and address_3 is null
and address_3 = ''
and address_3 = ' '
I tried using char_length, ascii functions and they return nothing for that filed value. Anyone have any ideas?
could also be that id = 249122 is not true for that combination. What happens when you do
select *
from ss.mailer_data
where address_3 is null
or
select *
from ss.mailer_data
where address_3 = ''
NULL is a special case - It's undefined but if that were your problem, Is Null should've found it
Can I ask you what data IS actually stored in Address3?
If you just want all records where address3 is empty/null,
select *
from ss.mailer_data
where
RTRIM(COALESCE(address_3, '')) =''
NB: LIKE is computationally expensive so use it carefully. Also, like '%%' is identical to Like '%'
Are you sure you had the right Id?

Zend: How to use 'not equal to' in WHERE clause?

I am using following zend code to select all data from a table where verified=1 and it is working for me.
$table = $this->getDbTable();
$select = $table->select();
$select->where('verified = 1');
$rows = $table->fetchAll($select);
No I want to select all data from that table where verified is not equal to '1'. I have tried the following ways but it is not fetching data.
$select->where('verified != 1');
$select->where('verified <> 1');
$select->where('verified != ?', 1);
Data structure for 'verified' column:
Field: verified
type: varchar(45)
Collation: utf8_bin
NULL: Yes
Default: NULL
Any idea that how to use 'not equal to' operator in WHERE clause in Zend? Thanks
$select->where('verified != ?', 1);
Real worls query example:
$query = $this->getDb()->select();
$query->from('title', array('title_id' => 'id', 'title', 'production_year', 'phonetic_code'))
->where('kind_id = 1')
->where('title = ?', trim($title))
->where('production_year != ?', '2009')
->limit(1)
;
Selects movies info from IMDB database. Works fine.
MySQL supports a custom operator <=> which returns true if the operands are equal or both null. It returns false if they are different, or if one operand is null.
$select->where('verified <=> 1');
This operator is non-standard. Standard SQL has syntax: IS NOT DISTINCT FROM that works just like MySQL's <=>.
Since your column is a varchar perhaps try where verified != '1' or verified is null
Can you show us the table structure for the table you are querying? Is the column verified an int or string? Also try printing the SQL statement that ZEND builds, see the echo line below.
$table = $this->getDbTable();
$select = $table->select();
$select->where('verified = 1');
echo sprintf("sql %s",$select);
$rows = $table->fetchAll($select);
Try :
$select->where('verified != ?', '1');
put quotation marks around the value. It is working for me.