Where and how to use NULLIF(X,Y) function of SQLITE? - sqlite

I know that NULLIF(X,Y) function of SQLITE work equivalent to:
CASE
WHEN
X = Y
THEN
NULL
ELSE
X
END
and IFNULL(X,Y) function work equivalent to:
CASE
WHEN
X IS NULL
THEN
Y
ELSE
X
END
IFNULL(X,Y) function of SQLITE is used for replacing the NULL values of X to the Y but I can't understand the use of NULLIF(X,Y) function of SQLITE.
Please explain with examples, so it is more useful.

The IFNULL function is used when the database contains NULL values, but you want to handle those values as something else; for example:
SELECT Name, IFNULL(Age, 'unknown') AS Age FROM People
The NULLIF function is used when the database contains special values that are not NULL, but that you want to handle as NULL.
This is useful especially for aggregate functions. For example, to get the number of employees that get bonuses, use:
SELECT COUNT(NULLIF(Bonus, 0)) FROM Employees
This is the same as:
SELECT COUNT(*) FROM Employees WHERE Bonus != 0
In practice, NULLIF is not used as often as IFNULL.

I use NULLIF() when UPDATing or INSERTing rows containing NULLable fieds.
Basically with PHP :
$value1 = 'foo';
$value2 = '';
$sql = 'INSERT INTO table(field1, field2) '
. "VALUES(NULLIF('$value1', ''), NULLIF('$value2', ''))";
// => INSERT INTO table(field1, field2) VALUES(NULLIF('foo', ''), NULLIF('', ''))
// => INSERT INTO table(field1, field2) VALUES('foo', NULL)
It saves me doing things like :
$value1forSQL = ($value1 === '' || $value1 === NULL) ? 'NULL' : "'$value1'";
...
$sql = ...
. "VALUES($value1forSQL, ...)";

Related

How to add a count number to an array of data : Doctrine

In fact, after returning a result of data from the database using Doctrine,
I'm trying to add the row count number, without calling another query request.
This is my function:
public function search(QueryBuilder $qb, string $search)
{
$qb = $qb->addSelect('COUNT(n) as count');
$search = $this->escape($search);
$qb->andWhere(
$qb->expr()->like('n.title', $qb->expr()->literal('%'.$search.'%'))
);
$qb->setMaxResults(2);
}
This is my DQL:
SELECT n, COUNT(n) as count FROM CoreBundle\Entity\News n LEFT JOIN n.category c WHERE n.title LIKE '%re%'
And I need to return as a result a all my data with a count key that refer to the number of rows.
The problem that I'm getting only the first row with id = 1, and it seems that the count number is correct.
So the result should by something like that:
['count' => 2 , [Newsn1,Newsn2]
Don't tell me to use array_count because I need to get the count of rows in the database, and I have a setMaxResults function, so I will not get a real number of rows.
I don't know the configuration of your table, I just can imagine. So, here's my try:
For getting counts for all titles in your table:
# SQL
SELECT COUNT(id) AS count, GROUP_CONCAT(title SEPARATOR ', ') AS titles FROM newses GROUP BY title
# DQL. assuming you are using a Repository method:
$qb = $this->createQueryBuilder('n');
$qb
->select("COUNT(n.id) AS count, GROUP_CONCAT(n.title SEPARATOR ', ') AS titles")
->leftJoin('n.category', 'c')
->groupBy('n.title')
;
return $qb->getQuery()->getArrayResult();
For getting counts for a particular title:
# SQL
SELECT COUNT(id) AS count, GROUP_CONCAT(title SEPARATOR ', ') AS titles FROM newses WHERE n.title LIKE '%news%' GROUP BY title
# NewsRepository.php
public function getTitlesCount($title)
{
$qb = $this->createQueryBuilder('n');
$qb
->select("COUNT(n.id) AS count, GROUP_CONCAT(n.title SEPARATOR ', ') AS titles")
->leftJoin('n.category', 'c')
->where('n.title LIKE :title')
->setParameter('title', "%{$title}%")
->groupBy('n.title')
;
return $qb->getQuery()->getArrayResult();
}

Simple Procedure raise ORA-06502

There's the simplified version of my code who keep raise me ORA-06502:
declare
p_filter varchar2(300) := '2012';
p_value varchar2(300) := '12345.000';
w_new_value number(13,3) := null ;
w_count number(4) := null ;
BEGIN
SELECT count(*)
INTO w_count
FROM dual
where p_filter = p_filter;
--- more filters
if w_count != 0 then
w_new_value := p_value / w_count;
else
w_new_value := p_value;
end if;
-- do something
end;
/
Someone can give me a help?
DataBase Details
nls_language = italian
nls_territory = italy
nls_currency = �
nls_iso_currency = italy
nls_numeric_characters = ,.
nls_calendar = gregorian
nls_date_format = dd-mon-rr
nls_date_language = italian
nls_characterset = we8iso8859p15
nls_sort = west_european
nls_time_format = hh24:mi:ssxff
nls_timestamp_format = dd-mon-rr hh24:mi:ssxff
nls_time_tz_format = hh24:mi:ssxff tzr
nls_timestamp_tz_format = dd-mon-rr hh24:mi:ssxff tzr
nls_dual_currency = �
nls_nchar_characterset = al16utf16
nls_comp = binary
nls_length_semantics = byte
nls_nchar_conv_excp = false
First, this is always going return a value of 1.
SELECT count(*)
INTO w_count
FROM dual
It doesn't matter what the qualifier is.
Lastly, I just ran your simplified code example in Oracle 11R2 and it didn't throw an exception.
I added the following statement in place of your "do something" comment:
dbms_output.put_line('w_new_value: ' || w_new_value || '. w_count: ' || w_count);
The result was:
w_new_value: 12345. w_count: 1
So, I think you've simplified your example into oblivion. You need to provide something that actually shows the error.
Good luck.
I found myself the ansewer and i think is useful for other know.
The real problem of the script for my DB is the language.
The italian "version" of Oracle accept , instead of the . for translate the VARCHAR2 into NUMBER unlike the most of other country.
For make the code running well the solution is
w_new_value := replace(p_value,'.',',') / w_count;
This trick finally allows the DB use my VARCHAR2 param like a NUMBER

Parsing large text in xml format in Pl/Sql

I have a log table and the table has a varchar2 field which holds xml string like below:
In this example ClientName attribute did not change but Clientsurname changed.
I want to capture changed columns and their previous and new values.
The log table contains millions of records.
Which method can you suggest for parsing this data in an efficient way?
<r>
<columntag nameattribute="ClientName">
<new_value>Jeffrey</new_value>
<previous_value>Jeffrey</previous_value>
</columntag>
<columntag nameattribute="ClientSurname">
<new_value>Dijk</new_value>
<previous_value>Disk</previous_value>
</columntag>
</r>
Thank you
not 100% sure the below is what you are after but it should give you some ideas about how to go about it. Hope it is helpfull
CREATE TABLE "RM4SERV"."LOG_TEST" ( "TESTLOG" VARCHAR2(4000 BYTE))
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Disk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jim</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijks</new_value><previous_value>Diskett</previous_value></columntag></r>');
declare
v_logrec varchar2(4000) := null;
v_recnum number := 0;
cursor c_logs is
select testlog from log_test;
cursor c_records is
select extractValue(x.column_value, '/columntag/#nameattribute') as column_name,
extractValue(x.column_value, '/columntag/new_value') as new_value,
extractValue(x.column_value, '/columntag/previous_value') as previous_value
from TABLE(XMLSequence(extract(xmltype.createxml(v_logrec), '//columntag'))) x
where extractValue(x.column_value, '/columntag/new_value') != extractValue(x.column_value, '/columntag/previous_value');
begin
for v_log in c_logs loop
v_logrec := v_log.testlog;
v_recnum := v_recnum + 1;
dbms_output.put_line(v_recnum);
for v_rec in c_records loop
SYS.dbms_output.put_line(v_rec.column_name || ' : *' || v_rec.new_value || '* : *' || v_rec.previous_value || '*');
end loop;
end loop;
end;
This would give you the below output (so Surname different in first record, nothing different in the second and both different in the third)...
1
ClientSurname : Dijk : Disk
2
3
ClientName : Jeffery : Jim
ClientSurname : Dijks : Diskett

Condensing several SQL queries into a single query so a parameter can be used for different data types

I'm looking to Condense several SQL Server queries into a single query so a parameter can be used for different data types. These types are dates, or numbers or strings. The parameter is called: #SearchValue.
In a strongly typed DataSet we have the 3 queries listed below.
This is for ASP.Net with a VB.Net code-behind file but I think this question is may also be good for non ASP.Net as well.
I call this one if the user enters a date into a search TextBox:
Query:
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments
WHERE (ParentID = #ParentID) AND
(PaymentDate = #SearchValue)
Call from VB.Net code-behind for the date search query:
tblObject = theTableAdapter.GetDataByPaymentDate(dcmParentsId, TextBoxSearch.Text)
If tblObject.Count() > 0 Then
GridViewSummary.DataSource = tblObject
GridViewSummary.DataBind()
End If
The other ones are for numbers only and the last one is for everything else.
This one is for numbers only:
SELECT PaymentDate, PaymentAmount, WhatWasPaymentFor, ID
FROM Payments
WHERE (ParentID = #ParentID) AND
(PaymentAmount = #SearchValue)
This one is called when the other 2 queries don't find any data:
SELECT PaymentDate, PaymentAmount, WhatWasPaymentFor, ID
FROM Payments
WHERE (ParentID = #ParentID) AND
((WhatWasPaymentFor LIKE '%' + #SearchValue + '%') OR
(#SearchValue = 'ALL'))
All of this coding works as is and I did it this way because there would be an error if I tried to call .GetDataByPaymentDate with a non date value.
Is there a way to use a single query to handle the searching by dates, numbers, and strings?
* UPDATES *
Thanks for all the sample queries. I am trying all of the sample queries in the SQL Server Management Studio to see what results come up.
I this one based on Gordon's query but it does not return any data:
DECLARE #SearchValue VARCHAR = '01/01/2012'
DECLARE #SearchType VARCHAR = 'Dates'
DECLARE #ParentID INT = 3
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments cross join
(select #SearchValue as sv) const
WHERE ParentID = #ParentID AND
(case when #SearchType = 'Dates' and ISDATE(const.sv) = 1
then (case when PaymentDate = CAST(const.sv AS datetime) then 'true' else 'false' end)
when #SearchType = 'Numbers' and ISNUMERIC(const.sv) = 1
then (case when PaymentAmount = cast(const.sv as Int) then 'true' else 'false' end)
when #SearchType = 'Everything Else'
then (case when WhatWasPaymentFor LIKE '%' + const.sv + '%' OR const.sv='ALL' then 'true' else 'false' end)
end) = 'true'
This is based on the one from gh9 and pulls up data. Thanks gh9:
DECLARE #SearchValue VARCHAR = 'Books'
DECLARE #ParentID INT = 3
DECLARE #PaymentDate DATETIME = NULL
DECLARE #PaymentAmount MONEY = NULL
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments
WHERE ParentID = #ParentID
AND (#paymentDate is null OR PaymentDate = #Paymentdate)
AND (#paymentAmount is null OR paymentAmount = #paymentAmount)
AND ((#SearchValue is null OR
(WhatWasPaymentFor LIKE '%' + #SearchValue + '%' OR #SearchValue='ALL'))
)
This lets you take advantage of not having to cast searchvalue to whatever you need, also is a bit more readable. Modify the syntax as needed, but the key idea is to use sql server ability to have null parameters and short circuit logic to evaluate strongly typed parameters instead of casting to the data type you need.
#ParentID INT
#PaymentAmount INT = NULL
#PaymentDate Datetime = null
#GenericSearchTerm varchar(100) = null
AS
BEGIN
SELECT
ID,
PaymentAmount,
PaymentDate,
WhatWasPaymentFor
FROM Payments
WHERE #ParentID = #ParentID
AND ( (#paymentDate is null OR PaymentDate = #Paymentdate))
AND (#paymentAmount is null OR paymentAmount = #paymentAmount))
AND ( #GenericSearchTerm is null OR ((WhatWasPaymentFor LIKE '%' + #GenericSearchTerm + '%' OR #SearchValue='ALL'))
EDIT:updated answer per #andriyM comments
How about adding a third parameter of #SearchType with possible values of ('datetime','int', or 'nvarchar') and using it in your WHERE clause to cast the #SearchValue to appropriate type for comparison. Something like:
SELECT
ID,
PaymentAmount,
PaymentDate,
WhatWasPaymentFor
FROM Payments
WHERE #ParentID = #ParentID
AND (
(#SearchType = 'datetime' AND PaymentDate = CAST(#SearchValue AS datetime))
OR
(#SearchType = 'int' AND PaymentAount = CAST(#SearchValue AS int))
OR
(#SearchType = 'nvarchar' AND
(WhatWasPaymentFor LIKE '%' + #SearchValue + '%' OR #SearchValue='ALL')
);
This is inspired by Bstateham's answer, but it does two things differently. First, it stores the values in a table, turning them from constants into variables. Second, it uses the case because that guarantees shortcircuiting.
This results in something like:
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments cross join
(select #SearchValue as sv) const
WHERE #ParentID = #ParentID AND
(case when #SearchType = 'datetime' and ISDATE(const.sv) = 1
then (case when PaymentDate = CAST(const.sv AS datetime) then 'true' else 'false' end)
when #SearchType = 'int' and ISNUMERIC(const.sv) = 1
then (case when PaumentAmount = cast(const.sv as Int) then 'true' else 'false' end)
when #SearchType = 'nvarchar'
then (case when WhatWasPaymentFor LIKE '%' + const.sv + '%' OR const.sv='ALL' then 'true' else 'false' end)
end) = 'true'
I also added in validation checks for the conversion to integer and date (not perfect, but will help). Also, you should probably name the #SearchType after the destination field rather than the type. However, I've kept the version with the type name.

SQL Select fields with a value of 'Y' and order by date descending, then select all others and order by another field ascending

I am generating an SQL query:
SELECT * FROM ToDoList
WHERE ws_status <> 'Completed'
AND (user_id= 'TESTUSR' OR ww_cover='TESTUSR'
OR (ws_status = 'Orphan' AND wwt_workgroupid IN (108)))
**ORDER BY psc_alt_code ASC**
And I need to list all results with wi_urgent set to 'Y' and order them by date Desc *first and then list all other results ordered by psc_alt_code descending* so I thought something like this would suffice:
ORDER BY (wi_urgent = 'Y') DESC, psc_alt_code ASC
I am getting SqlClient.SqlException: Incorrect syntax near '=' error when trying to run that query. Please note that I am querying an SQL View if that makes a difference?
You can use a case expression in the order by
SELECT * FROM ToDoList
WHERE ws_status <> 'Completed'
AND (user_id= 'TESTUSR' OR ww_cover='TESTUSR'
OR (ws_status = 'Orphan' AND wwt_workgroupid IN (108)))
ORDER BY CASE WHEN wi_urgent = 'Y' THEN 0 ELSE 1 END ASC
,psc_alt_code
I don't think you can do wi_urgent = 'Y' in an ORDER BY.
Since you're looking for all results with wi_urgent, try adding it to the WHERE clause:
SELECT * FROM ToDoList
WHERE ws_status <> 'Completed'
AND (user_id= 'TESTUSR' OR ww_cover='TESTUSR'
OR (ws_status = 'Orphan' AND wwt_workgroupid IN (108)))
AND wi_urgent = 'Y'
ORDER BY wi_urgent DESC,
psc_alt_code ASC

Resources