MySQL if condition null - mysql

I am having database columns
table name:
sales
trans_details
Initially data will be inserted into sales lateron when quantity dispatched trans_details table get updated
columns in sales
total_quantity
e.t.c
columns in trans_details
ordered_quantity
dispatched_quantity
pending_quantity
e.t.c
i want to display all the values:
- ordered_quantity
- dispatched_quantity
- pending_quantity
SELECT
IF(trans.ordered_quantity!='',trans.ordered_quantity,(sorder.total_quantity)) AS quantity,
IF(trans.dispatched!='',trans.dispatched,0) AS today_dispatched_qty,
IF(trans.dispatched!='',trans.dispatched,0) AS dis_qty,
IF(trans.Pending_quantity!='',trans.Pending_quantity,sorder.total_quantity) AS pending_qty
FROM
sales as sorder
LEFT OUTER JOIN trans_details as trans
the query is working fine but when the quantity fully dispatched it should '0' but right now it is showing total_quantity... and when i replace sorder.total_quantity with '0' in this condition IF(trans.Pending_quantity='0',trans.Pending_quantity,sorder.total_quantity) AS pending_qty... initially it is showing '0' but it should show the total_quantity...
sample output:
total_quantity..........dispatched_quantity.......pending_quantity
50 45 5
5 5 0
5 0 5

My guess is that you have NULL values in your data. If the issue is NULLs and the data types are numeric, then try this:
SELECT coalesce(trans.ordered_quantity,sorder.total_quantity) AS quantity,
coalesce(trans.dispatched,0) AS today_dispatched_qty,
coalesce(trans.dispatched,0) AS dis_qty,
coalesce(trans.Pending_quantity,sorder.total_quantity) AS pending_qty
If these really are strings, then you need to add in a NULL check. I would encourage you to use case, which is standard SQL, rather than if:
select (case when trans.ordered_quantity is not null and trans.ordered_quantity <> ''
then trans.ordered_quantity
else sorder.total_quantity
end) as quantity,
. . .
And, finally, I am assuming that you just left off the on clause by accident. In any database other than MySQL, you would get a parse error. But, as a good habit, you should always have an on clause when specifying an inner or outer join.

Related

mysql - query to extract report from book register

I have the below query in mysql, when I run the query, it gives me the complete report and "where clause does not work"
SELECT oo.dateaccessioned AS 'Date',
oo.barcode AS 'Acc. No.',
ooo.title AS 'Title',
ooo.author AS 'Author/Editor',
concat_ws(' , ', o.editionstatement, oo.enumchron) AS 'Ed./Vol.',
concat_ws(' ', o.place, o.publishercode) AS 'Place & Publisher',
ooo.copyrightdate AS 'Year', o.pages AS 'Page(s)',
ooooooo.name AS 'Source',
oo.itemcallnumber AS 'Class No./Book No.',
concat_ws(', ₹', concat(' ', ooooo.symbol, oooo.listprice), oooo.rrp_tax_included) AS 'Cost',
concat_ws(' , ', oooooo.invoicenumber, oooooo.shipmentdate) AS 'Bill No. & Date',
'' AS 'Withdrawn Date',
'' AS 'Remarks'
FROM biblioitems o
LEFT JOIN items oo ON oo.biblioitemnumber=o.biblioitemnumber
LEFT JOIN biblio ooo ON ooo.biblionumber=o.biblionumber
LEFT JOIN aqorders oooo ON oooo.biblionumber=o.biblionumber
LEFT JOIN currency ooooo ON ooooo.currency=oooo.currency
LEFT JOIN aqinvoices oooooo ON oooooo.booksellerid=oo.booksellerid
LEFT JOIN aqbooksellers ooooooo ON ooooooo.id=oo.booksellerid
WHERE cast(oo.barcode AS UNSIGNED) BETWEEN <<Accession Number>> AND <<To Accession Number>>
GROUP BY oo.barcode
ORDER BY oo.barcode ASC
Can you please help me to generate a report based on above query - oo.barcode (it is a varchar). I am a Library team member than a database administrator. My oo.barcode begins with HYD and then numercs. I know if it(oo.barcode) is a number only field the above query works without any issue.
I search about how cast works but not able to understand as i am not into database administration.
If the barcode column is VARCHAR and begins with "HYD", CAST AS UNSIGNED will cause a value of HYD123 to result in 0.
The non-numeric characters of the string would need to be removed prior to casting the value as an integer.
This can be achieved by trimming the leading text "HYD" from the barcode.
CAST(TRIM(LEADING 'HYD' FROM barcode) AS UNSIGNED)
Otherwise, if the prefix is always 3 characters, the substring position of barcode can be used.
CAST(SUBSTR(barcode, 4) AS UNSIGNED)
If any other non-numeric characters are contained within the string, such as HYD-123-456-789, HYD123-456-789PT, HYD123-456.789, etc, they will also needed to be removed, as the type conversion will treat them in unexpected ways.
In addition, any leading 0's of the resulting numeric string value will be truncated from the resulting integer, causing 0123 to become 123.
For more details on how CAST functions see: 12.3 Type Conversion in Expression Evaluation
Examples db<>fiddle
CREATE TABLE tester (
barcode varchar(255)
);
INSERT INTO tester(barcode)
VALUES ('HYD123'), ('HYD0123'), ('HYD4231');
Results
SELECT cast(barcode AS UNSIGNED)
FROM tester;
cast(barcode AS UNSIGNED)
0
0
0
SELECT CAST(TRIM(LEADING 'HYD' FROM barcode) AS UNSIGNED)
FROM tester;
CAST(TRIM(LEADING 'HYD' FROM barcode) AS UNSIGNED)
123
123
4231
SELECT barcode
FROM tester
WHERE CAST(TRIM(LEADING 'HYD' FROM barcode) AS UNSIGNED) BETWEEN 120 AND 4232;
barcode
HYD123
HYD0123
HYD4231
SELECT CAST(SUBSTR(barcode, 4) AS UNSIGNED)
FROM tester;
CAST(SUBSTR(barcode, 4) AS UNSIGNED)
123
123
4231
SELECT barcode
FROM tester
WHERE CAST(SUBSTR(barcode, 4) AS UNSIGNED) BETWEEN 120 AND 4232;
barcode
HYD123
HYD0123
HYD4231
JOIN optimization
To obtain the expected results, you most likely want an INNER JOIN of the items table with an ON criteria matching the desired barcode range condition. Since INNER JOIN is the equivalent of using WHERE oo.barcode IS NOT NULL, as is the case with your current criteria - NULL matches within the items table are already being excluded.
INNER JOIN items AS oo
ON oo.biblioitemnumber = o.biblioitemnumber
AND CAST(SUBSTR(oo.barcode, 4) AS UNSIGNED) BETWEEN ? AND ?
Full-Table Scanning
It is important to understand that transforming the column value to suit a criteria will cause a full-table scan that does not benefit from indexing, which will run very slowly.
Instead it is best to store the integer only version of the value in the database to see the benefits of indexing.
This can be accomplished in many ways, such as generated columns.
GROUP BY without an aggregate
Lastly, you should avoid using GROUP BY without an aggregate function. You most likely are expecting DISTINCT or similar form of limiting the record set. Please see MySQL select one column DISTINCT, with corresponding other columns on ways to accomplish this.
To ensure MySQL is not selecting "any value from each group" at random (leading to differing results between query executions), limit the subset data to the distinct biblioitemnumber column values from the available barcode matches. One approach to accomplish the limited subset is as follows.
/* ... */
FROM biblioitems o
INNER JOIN (
SELECT biblioitemnumber, barcode, booksellerid, enumchron, itemcallnumber
FROM items WHERE biblioitemnumber IN(
SELECT MIN(biblioitemnumber)
FROM items
WHERE CAST(SUBSTR(barcode, 4) AS UNSIGNED) BETWEEN ? AND ?
GROUP BY barcode
)
) AS oo
ON oo.biblioitemnumber = o.biblioitemnumber
LEFT JOIN biblio ooo ON ooo.biblionumber=o.biblionumber
LEFT JOIN aqorders oooo ON oooo.biblionumber=o.biblionumber
LEFT JOIN currency ooooo ON ooooo.currency=oooo.currency
LEFT JOIN aqinvoices oooooo ON oooooo.booksellerid=oo.booksellerid
LEFT JOIN aqbooksellers ooooooo ON ooooooo.id=oo.booksellerid
ORDER BY oo.barcode ASC
Try this :
...
WHERE cast(SUBSTRING_INDEX(oo.barcode,'HYD',-1) AS UNSIGNED INTEGER) BETWEEN <<Accession Number>> AND <<To Accession Number>>
...
SUBSTRING_INDEX(oo.barcode,'HYD',-1) will transform HYD132453741 to 132453741
demo here

mySQL Sum Production_Needed Group BY Part_ID

Want to generate a result of Open orders where Production is needed. At issue is each part may have more than one open order. With the GROUP BY my code gives me only one order but does give me the total Production_Needed (which is also a negative in value for orders with enough inventory).
Does my SUM(...) as Production_Needed need to be in the WHERE ?
Thanks,
SELECT part.part_ID AS Part_Part_ID,
part.Inventory, part.part_number,
ord.part_id AS Order_Part_ID,
ord.order_type, ord.quantity_ordered, ord.quantity_shipped,
SUM(ord.quantity_ordered - ord.quantity_shipped - part.Inventory) AS Production_Needed
FROM production_orders ord
JOIN production_part part ON ord.part_ID = part.part_ID
WHERE ord.is_Active = True AND ord.order_type = 0
GROUP BY Order_Part_ID
ORDER BY part.part_number ASC
Data Production_Part part
Part_ID
Part_Inventory
Part_Number
1
12500
97-528
2
0
FC2569
3
1000
39367
Data Production_Orders Ord
Order_Part_ID
Order_Type
Quantity_Ordered
Quantity_Shipped
1
0
8000
0
2
0
1000
500
2
0
1000
0
3
1
10
0
Desired Result - Only Parts that need production
Part_ID
Quantity_Ordered
Quantity_Shipped
2
1000
500
2
1000
0
Untested: need a sampled data set and structure for testing:
This creates an inline view and totals the inventory order amounts then stubtracts it from the inventory to determine if there is a production needed to fulfil open orders. I'd have to use some additional analytical functions if we needed to do this on an order by order basis however; or join these results back into the orders...
--Show parts which lack inventory to fullfill outstanding open orders.
SELECT
P.Part_ID as Part_Part_ID
, P.Inventory
, P.Part_Number
, O.Part_ID as Order_Part_ID
, UnDel_Units-coalesce(P.Inventory,0) as Production_Needed --use coalesce incase no part record exists for some reason.
FROM Production_Part P
RIGHT JOIN ( --use right join just incase part record doesn't exist for some reason
SELECT part_ID, SUM(quantity_ordered-quantity_shipped) as UnDel_Units
FROM PRODUCTION_ORDERS
WHERE IS_ACTIVE=TRUE
and ORDER_TYPE=0
GROUP BY PART_ID) O --derived table "O" for orders showing sum ottal by part of units undelivered
on O.Part_ID=P.Part_ID
WHERE UnDel_Units > coalesce(P.Inventory,0)
-- If inventory is > undelivered units for the part, ignore as additional production isn't needed

How to replace one value calculated by COUNT with another value in SELECT result?

Suppose there are two tables
shop seller
------ -------
id id
seller_id seller_name
goods sallary
seller_id may be repeated, parts from seller.id may not be there at all. So, I need to get the result of the request consisting of saller_name and counting the number of stores on each seller. I did it like this
SELECT seller_name, COUNT(seller_id) AS shops FROM seller
LEFT JOIN shop ON seller.id = shop.seller_id GROUP BY seller_name.
And it works. But the problem is that I need to replace all 0 in shops with the text 'none'.
I tried to do it through
REPLACE(COUNT(seller_id),0,"none")
and with
CASE
WHEN COUNT(seller_id)=0 THEN "none"
ELSE COUNT(seller_id)
END
But instead of the expected numbers and none, I get a random set of numbers and letters. How to do it?
The problem is that 'none' is a string, but the value is a count. I would strongly advise you to stick with numbers, but that is not your query.
You need to do type conversion:
SELECT se.seller_name,
(CASE WHEN COUNT(s.seller_id) = 0
THEN 'none'
ELSE CAST(COUNT(s.seller_id) AS VARCHAR(255))
END) AS shops
FROM seller se LEFT JOIN
shop s
ON se.id = sh.seller_id
GROUP BY se.seller_name;
In MySQL, use CAST(COUNT(s.seller_id) AS CHAR). Or almost any database supports CONCAT(COUNT(s.seller_id), ''), but using CONCAT() to convert types seems misleading to me.
Point 1: REPLACE(COUNT(seller_id),0,"none") --It will replace all zero's with 'none'.
Like your count is 10 it will return 1none in output.
Point 2: CASE WHEN COUNT(seller_id)=0 THEN "none" ELSE COUNT(seller_id) END--It will gives you conversion failed error.
So you need you cast the count in varchar as given below.
SELECT se.seller_name,
(CASE WHEN COUNT(s.seller_id) = 0
THEN 'none'
ELSE CAST(COUNT(s.seller_id) AS VARCHAR(10))
END) AS shops
FROM seller se LEFT JOIN
shop s
ON se.id = sh.seller_id
GROUP BY se.seller_name;
You need to change yor query to something like this:
SELECT seller_name,
CASE
WHEN COUNT(shop.Id)=0 THEN 'none'
ELSE CAST(COUNT(shop.Id) as NVARCHAR(10))
END AS shops FROM seller
LEFT JOIN shop ON seller.id = shop.seller_id GROUP BY seller_name

SELECT CASE, COUNT(*)

I want to select the number of users that has marked some content as favorite and also return if the current user has "voted" or not. My table looks like this
CREATE TABLE IF NOT EXISTS `favorites` (
`user` int(11) NOT NULL DEFAULT '0',
`content` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`user`,`content`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
Say I have 3 rows containing
INSERT INTO `favorites` (`user`, `content`) VALUES
(11, 26977),
(22, 26977),
(33, 26977);
Using this
SELECT COUNT(*), CASE
WHEN user='22'
THEN 1
ELSE 0
END as has_voted
FROM favorites WHERE content = '26977'
I expect to get has_voted=1 and COUNT(*)=3 but
I get has_voted=0 and COUNT(*)=3. Why is that? How to fix it?
This is because you mixed aggregated and non-aggregated expressions in a single SELECT. Aggregated expressions work on many rows; non-aggregated expressions work on a single row. An aggregated (i.e. COUNT(*)) and a non-aggregated (i.e. CASE) expressions should appear in the same SELECT when you have a GROUP BY, which does not make sense in your situation.
You can fix your query by aggregating the second expression - i.e. adding a SUM around it, like this:
SELECT
COUNT(*) AS FavoriteCount
, SUM(CASE WHEN user=22 THEN 1 ELSE 0 END) as has_voted
FROM favorites
WHERE content = 26977
Now both expressions are aggregated, so you should get the expected results.
Try this with SUM() and without CASE
SELECT
COUNT(*),
SUM(USER = '22') AS has_voted
FROM
favorites
WHERE content = '26977'
See Fiddle Demo
Try this:
SELECT COUNT(*), MAX(USER=22) AS has_voted
FROM favorites
WHERE content = 26977;
Check the SQL FIDDLE DEMO
OUTPUT
| COUNT(*) | HAS_VOTED |
|----------|-----------|
| 3 | 1 |
You need sum of votes.
SELECT COUNT(*), SUM(CASE
WHEN user='22'
THEN 1
ELSE 0
END) as has_voted
FROM favorites WHERE content = '26977'
You are inadvertently using a MySQL feature here: You aggregate your results to get only one result record showing the number of matches (aggregate function COUNT). But you also show the user (or rather an expression built on it) in your result line (without any aggregate function). So the question is: Which user? Another dbms would have given you an error, asking you to either state the user in a GROUP BY or aggregate users. MySQL instead picks a random user.
What you want to do here is aggregate users (or rather have your expression aggregated). Use SUM to sum all votes the user has given on the requested content:
SELECT
COUNT(*),
SUM(CASE WHEN user='22' THEN 1 ELSE 0 END) as sum_votes
FROM favorites
WHERE content = '26977';
You forgot to wrap the CASE statement inside an aggregate function. In this case has_voted will contain unexpected results since you are actually doing a "partial group by". Here is what you need to do:
SELECT COUNT(*), SUM(CASE WHEN USER = 22 THEN 1 ELSE 0 END) AS has_voted
FROM favorites
WHERE content = 26977
Or:
SELECT COUNT(*), COUNT(CASE WHEN USER = 22 THEN 1 ELSE NULL END) AS has_voted
FROM favorites
WHERE content = 26977

MySQL return max value or null if one column has no value

I try to get the max value of a mysql select, but want to have it null/empty/0 if there is one row containing no timestamp.
Table stats (simplyfied):
ID CLIENT ORDER_DATE CANCEL_DATE
1 5 1213567200
2 5 1213567200
3 6 1210629600 1281736799
4 6 1210629600 1281736799
5 7 1201042800 1248386399
6 7 1201042800
7 8 1205449200 1271282399
I'm now looking to get the lowest order date (no problem, as it is never empty), and
the maximum cancel date. If the client has already cancelled his subscription, the cancel date is filled, but if he is still active, there is no cancel date at all.
Query:
SELECT ID, min(ORDER_DATE) AS OD, max(CANCEL_DATE) AS CD FROM stats GROUP BY CLIENT
Returns:
ID OD CD
5 1213567200 // fine
6 1210629600 1281736799 // fine
7 1201042800 1248386399 // Should be empty
8 1205449200 1271282399 // fine
I can't figure it out how to return empty/0/NULL if there is one (or more) empty colums for a client. Also tried with NULL fields.
Thanks for any hint.
I don't know how fast it will be but I guess it can be solved like this:
SELECT ID, min(ORDER_DATE) AS OD,
IF(COUNT(*)=COUNT(CANCEL_DATE),max(CANCEL_DATE),NULL) AS CD
FROM stats GROUP BY CLIENT
I couldn't test it but the idea behind this solution is that count(cancel_date) should count all not null value entries and if it's equal to count(*) that means that there are no null values and it will return max(cancel_date), otherwise null.
You could use a query like this:
SELECT
client,
min(ORDER_DATE) AS OD,
case when MAX(CANCEL_DATE IS NULL)=0 THEN max(CANCEL_DATE) END AS CD
FROM
stats
GROUP BY
CLIENT
Please see fiddle here.
CANCEL_DATE IS NULL will be evaluated either to 0, when CANCEL_DATE is not null, or to 1 when it is null
MAX(CANCEL_DATE IS NULL) will be evaluated to 0 if there are no cancel_date with null values, otherwise its value will be 1.
when MAX(CANCEL_DATE IS NULL)=0 it means that there are no rows where CANCEL_DATE is null, and we need to return MAX(cancel_date) in that case, otherwise we need to return NULL.