I have a function in Mysql that is called like this:
DB::raw("count_adults(rooms.id) as adults"),
count_adults is this:
BEGIN
DECLARE adults INT;
SELECT
count(*) INTO adults
FROM
clients
WHERE
clients.room_id = r_id
and (age >= 18
or age = 0);
RETURN adults;
END
However I want to if this query returns no results(0) do another select or function, like this:
SELECT count(*) INTO adults FROM client_room, clients
WHERE client_id = clients.id and client_room.room_id = r_id and (age >18 or age = 0)
Can I create a if condition? IN the php code? Or in the query? Something like this:
IF EXISTS (SELECT 1 FROM clients WHERE clients.room_id = r_id)
BEGIN
DECLARE adults INT;
SELECT
count(*) INTO adults
FROM
clients
WHERE
clients.room_id = r_id
and (age >= 18
or age = 0);
RETURN adults;
END
ELSE
BEGIN
DECLARE adults INT;
SELECT count(*) INTO adults
FROM
client_room, clients
WHERE
client_id = clients.id and client_room.room_id = r_id
and (age >18
or age = 0);
END
I have tried this and it seems to work:
BEGIN
DECLARE adults INT;
SELECT COUNT(*) INTO adults
FROM clients
WHERE clients.room_id = r_id
and (age >= 18 or age = 0);
RETURN IF( adults>0, adults, (SELECT count(*) FROM client_room, clients
WHERE client_id = clients.id
and client_room.room_id = r_id)
);
END
You could use CASE in the query, e.g.:
CASE
WHEN function1() > 0
THEN function1()
WHEN function2() > 0
THEN function2()
ELSE
0
END
That will evaluate to the value returned from function1() if it is larger than 0. If it's not but the value returned by function2() is larger then 0, then this is taken. Otherwise it's 0.
Instead of function1(), function2() etc. a column, subquery (that is, if it returns a scalar), literal, etc. can be used. And of course it can be changed to more or less conditions.
Related
I'm trying to select a group of list and get results in re-ordered number fashion.
e.g.
ID name
jack
jack
paul
bob
bob
paul
bob
Say I select name='bob' thus got its ID numbers 4,5,7
Now I want the results to be
in this order 1, 2, 3 ...
instead of 4, 5, 7 ...
because it's bob's first second and third, etc...
What's the easiest way to accomplish this?
Here mysql variables can do the work. Declare & initialize a variable #a = 0, assign an incremented value and select in query. The query should look like:
SET #a = 0;
SELECT #a := #a + 1 AS id, name FROM table_name WHERE name = 'bob';
SET #a = 0;
In the end, again set value of #a = 0.
If you are using Mysql 8 then you could use window function rank() and common table expression
with ordered_data as (
select *,
rank() over (partition by name order by id asc ) rnk
from your_table
order by rnk
)
select * from ordered_data where name = 'bob';
Or if you are using older release then you could use a correlated query to get the rank
select a.id,a.name,
(select count(*)
from your_table
where name= a.name
and id <= a.id) rnk
from your_table a
where a.name = 'bob'
order by a.id;
Demo
I have the PHP code:
$query = mysqli_query($mysqli, "SELECT * FROM `table_1`");
$result = mysqli_num_rows($query);
$queryTwo = mysqli_query($mysqli, "SELECT * FROM `table_2`");
$resultTwo = mysqli_num_rows($queryTwo);
$number = $result + $resultTwo;
return $number;
The point is that sometimes, the $number variable is returning NULL,
when it should not supposed to do that.
I have always rows in those 2 tables, and the returned result should not be NULL, ever.
Is this a correct approach to sum the number of rows from 2 tables? I don`t understand why sometimes I get NULL instead of a number.
Well, I would suggest you to do it like
select ( select count(*) from Table1 ) + ( select count(*) from Table2 )
as total_rows
executing this query and getting the value of total_rows will return you true result
Or you can create a stored procedure to do the same thing. as explained below
CREATE PROCEDURE sp_Test
AS
-- Create two integer values
DECLARE #tableOneCount int, #tableTwoCount int
-- Get the number of rows from the first table
SELECT #tableOneCount = (SELECT COUNT(*) FROM Table1
WHERE WhereClause)
SELECT #tableTwoCount = (SELECT COUNT(*) FROM Table2
WHERE WhereClause)
-- Return the sum of the two table sizes
SELECT TotalCount = #tableOneCount + #tableTwoCount
Why don't you go with only one query like this:
you will have the result directly in one step and it will avoid contacting the DB twice to fetch intermediate result and it will also simplify your program!
SELECT
(select count(*) from table_1)
+
(select count(*) from table_2)
I would like to better optimize my code. I'd like to have a single query that allows an alias name to have it's own limit and also include a result with no limit.
Currently I'm using two queries like this:
// ALL TIME //
$mikep = mysqli_query($link, "SELECT tasks.EID, reports.how_did_gig_go FROM tasks INNER JOIN reports ON tasks.EID=reports.eid WHERE `priority` IS NOT NULL AND `partners_name` IS NOT NULL AND mike IS NOT NULL GROUP BY EID ORDER BY tasks.show_date DESC;");
$num_rows_mikep = mysqli_num_rows($mikep);
$rating_sum_mikep = 0;
while ($row = mysqli_fetch_assoc($mikep)) {
$rating_mikep = $row['how_did_gig_go'];
$rating_sum_mikep += $rating_mikep;
}
$average_mikep = $rating_sum_mikep/$num_rows_mikep;
// AND NOW WITH A LIMIT 10 //
$mikep_limit = mysqli_query($link, "SELECT tasks.EID, reports.how_did_gig_go FROM tasks INNER JOIN reports ON tasks.EID=reports.eid WHERE `priority` IS NOT NULL AND `partners_name` IS NOT NULL AND mike IS NOT NULL GROUP BY EID ORDER BY tasks.show_date DESC LIMIT 10;");
$num_rows_mikep_limit = mysqli_num_rows($mikep_limit);
$rating_sum_mikep_limit = 0;
while ($row = mysqli_fetch_assoc($mikep_limit)) {
$rating_mikep_limit = $row['how_did_gig_go'];
$rating_sum_mikep_limit += $rating_mikep_limit;
}
$average_mikep_limit = $rating_sum_mikep_limit/$num_rows_mikep_limit;
This allows me to show an all-time average and also an average over the last 10 reviews. Is it really necessary for me to set up two queries?
Also, I understand I could get the sum in the query, but not all the values are numbers, so I've actually converted them in PHP, but left out that code in order to try and simplify what is displayed in the code.
All-time average and average over the last 10 reviews
In the best case scenario, where your column how_did_gig_go was 100% numeric, a single query like this could work like so:
SELECT
AVG(how_did_gig_go) AS avg_how_did_gig_go
, SUM(CASE
WHEN rn <= 10 THEN how_did_gig_go
ELSE 0
END) / 10 AS latest10_avg
FROM (
SELECT
#num + 1 AS rn
, tasks.show_date
, reports.how_did_gig_go
FROM tasks
INNER JOIN reports ON tasks.EID = reports.eid
CROSS JOIN ( SELECT #num := 0 AS n ) AS v
WHERE priority IS NOT NULL
AND partners_name IS NOT NULL
AND mike IS NOT NULL
ORDER BY tasks.show_date DESC
) AS d
But; Unless all the "numbers" are in fact numeric you are doomed to sending every row back from the server for php to process unless you can clean-up the data in MySQL somehow.
You might avoid sending all that data twice if you establish a way for your php to use only the top 10 from the whole list. There are probably way of doing that in PHP.
If you wanted assistance in SQL to do that, then maybe having 2 columns would help, it would reduce the number of table scans.
SELECT
EID
, how_did_gig_go
, CASE
WHEN rn <= 10 THEN how_did_gig_go
ELSE 0
END AS latest10_how_did_gig_go
FROM (
SELECT
#num + 1 AS rn
, tasks.EID
, reports.how_did_gig_go
FROM tasks
INNER JOIN reports ON tasks.EID = reports.eid
CROSS JOIN ( SELECT #num := 0 AS n ) AS v
WHERE priority IS NOT NULL
AND partners_name IS NOT NULL
AND mike IS NOT NULL
ORDER BY tasks.show_date DESC
) AS d
In future (MySQL 8.x) ROW_NUMBER() OVER(order by tasks.show_date DESC) would be a better method than the "roll your own" row numbering (using #num+1) shown before.
I've got a table with the following data.
table name: myTable
prodID catNo variable1 variable2
1 20 Cat Blue
2 10 Cat Red
2 15 Cat Green
2 20 Cat Black
3 20 Cat Yellow
4 10 Cat Orange
4 15 Cat Brown
4 20 Cat Black
5 30 Cat Pink
I want to be able to select all columns from myTable where the following is true "(prodID = 2 and catNo = 10) AND (prodID = 2 and catNo = 15)". Therefore getting a result of the two rows only if both conditions are met and it will return nothing if both rows aren't present.
So my results table will look like this.
table name: results
prodID catNo variable1 variable2
2 10 Cat Red
2 15 Cat Green
I've tried to use conditional if statements but can't seem to get them working in the sql. My current solution it to get back all rows with prodID = 2 and then using php to do the if statement to decide what to display but this won't work with the pagination I've designed for displaying the results as my limit will distort the number of results per page.
I know I could use 'having count rows=2' but I'm not sure how to word it.
If you want to get results only if records are found in catNo (10,15) but to also return 0 results if you where looking for catNo in (10,12)
SELECT * FROM `myTable`
WHERE (`prodID` = 2 AND `catNo` IN (10,15))
AND (SELECT COUNT(`catNo`) FROM `myTable` WHERE `prodID` = 2 AND `catNo` IN (10,15))>1;
For three CatNo's
SELECT * FROM `myTable`
WHERE (`prodID` = 4 AND `catNo` IN (10,15,20))
AND (SELECT COUNT(`catNo`) FROM `myTable` WHERE `prodID` = 4 AND `catNo` IN (10,15,20))>2;
SQL Fiddle
To match both categories for same product you can do so
select t.*
from table1 t
join (
select prodID
from table1
where catNo in (10,15)
and prodID = 2
group by prodID
having count(distinct catNo ) = 2
) t2
using(prodID)
where t.catNo in (10,15)
DEMO
Use the below SQL query.
SELECT * FROM myTable where prodID = 2 and (catNo = 10 OR catNo = 15)
Hope this helps you
Try this it will work :
SELECT * FROM `myTable` WHERE `prodID` = '2' AND `catNo` IN ('10','15')
SELECT DISTINCT a.*,b.catNo,c.catNo FROM myTable AS a
JOIN mytable AS b ON b.prodID=a.prodID
JOIN mytable AS c ON c.prodID=a.prodID
WHERE a.prodID=2 AND b.catNo=10
AND c.catNo=15;
this one worked. really tricky situation this problem is.
Most solutions posted are missing the part about returning nothing unless BOTH where conditions are met. As such most solutions will fail if one and only one condition is met, as they will still return one row instead of none.
Assuming you want to be able to check for any number of WHERE conditions, and not just two, you could use a sub query based solution.
Using this approach you can check the number of qualifying rows within the result set prior to output. There are a few ways to do this but the idea is to compare the number of qualifying rows against your expected number of rows. If they match, output the rows. If they do not match, return an empty result set.
M Khalid Junaid has posted a solution using a group by with a having condition within a sub query to check that the result set size is 2. This solution will work for your specific example and others like it. It might not if your WHERE conditions become more complex or varied as it relies on ProdID being the same in each WHERE condition, but the principal is sound. Also, this particular method requires the WHERE condition be processed against the source table twice, whereas the one below only does so once. For very large tables and/or many WHERE conditions this method should be more efficient.
You can construct sub queries either directly in the FROM condition as in other examples, or via WITH statements. I've used the later here as it more clearly shows what is going on:
With ResultSet as (select * from myTable
where (prodID = 2 AND catNo = 10) OR (prodID = 2 AND catNo = 15)), -- your WHERE condition
TotalRows as (select COUNT(*) as TRows from ResultSet) -- count of result set
select ResultSet.*
from ResultSet
inner join TotalRows on 1=1 -- force the join to work no matter what the tables contain
where TRows = 2 -- this is where you check against how many result rows you expect
The "ResultSet" sub query is where your WHERE cause goes, and it can be as simple or complex as required. "TotalRows" counts the result set, and therefore contains only one row. You then join the sub queries together but only output rows from the former if the result set count (TRows) matches your expected row size from the later.
You can try writing a function. It's quite long but quite easy to understand
DROP FUNCTION getIt();
CREATE OR REPLACE FUNCTION getIt()
RETURNS TABLE (prodID INTEGER, catNo INTEGER, variable1 TEXT, variable2 TEXT) AS
$BODY$
BEGIN
IF (CAST ((SELECT "prodID"
FROM "myTable"
where "prodID"=2 and "catNo" = 10) AS INTEGER)>0 AND CAST ((SELECT "prodID"
FROM "myTable"
where "prodID"=2 and "catNo" = 15) AS INTEGER)>0)
THEN RETURN QUERY SELECT *
FROM "myTable"
where "prodID"=2 and ("catNo" = 10 or "catNo" = 15 );
END IF;
END;
$BODY$
LANGUAGE 'plpgsql';
SELECT * FROM getIt();
It can be parameterized
DROP FUNCTION getIt(pid integer, cid1 integer, cid2 integer);
CREATE OR REPLACE FUNCTION getIt(pid integer, cid1 integer, cid2 integer)
RETURNS TABLE (prodID INTEGER, catNo INTEGER, variable1 TEXT, variable2 TEXT) AS
$BODY$
BEGIN
IF (CAST ((SELECT "prodID"
FROM "myTable"
where "prodID"=$1 and "catNo" = $2) AS INTEGER)>0 AND CAST ((SELECT "prodID"
FROM "myTable"
where "prodID"=$1 and "catNo" = $3) AS INTEGER)>0)
THEN RETURN QUERY SELECT *
FROM "myTable"
where "prodID"=$1 and ("catNo" = $2 or "catNo" = $3 );
END IF;
END;
$BODY$
LANGUAGE 'plpgsql';
SELECT * FROM getIt(2, 10, 15);
Thank you M Khalid Junaid's answer works. I've managed to come up with a solution that doesn't require a join which with a very large table like mine will be it would be rather slow:
select *
from myTable
where ((prodID = 2 and catNo = 10) or (prodID = 2 and catNo = 15))
group by catNo
having (
select count(distinct(catNo))
from myTable
where ((prodID = 2 and catNo = 10) or (prodID = 2 and catNo = 15))
)=2;
The condition should be (prodID = 2 and catNo = 10) OR (prodID = 2 and catNo = 15)
trying to rename duplicates in MySQL database so far using that code but this only adding 1 at the end of name. So if I have
UPDATE phpfox_photo n
JOIN (SELECT title_url, MIN(photo_id) min_id FROM phpfox_photo GROUP BY title_url HAVING COUNT(*) > 1) d
ON n.title_url = d.title_url AND n.photo_id <> d.min_id
SET n.title_url = CONCAT(n.title_url, '1');
Anna
Anna
Anna
Result is
Anna
Anna1
Anna11
When I got 200 Annas result is Anna1111111111111111111111111111111111111111111....etc
how do I do it to rename in the following inc
Anna
Anna1
Anna2
if i didn't miss something you can make a stored procedure that iterates throw your rows using cursors to do that as following:
DECLARE counter INT DEFAULT 0;
DECLARE num_rows INT DEFAULT 0;
DECLARE offset INT;
DECLARE title_urlvalue VARCHAR(50);
DECLARE no_more_rows BOOLEAN;
DECLARE ucur CURSOR FOR
SELECT
UPDATE phpfox_photo n
JOIN (SELECT title_url, MIN(photo_id) min_id
FROM phpfox_photo GROUP BY title_url HAVING COUNT(*) > 1) d
ON n.title_url = d.title_url AND n.photo_id <> d.min_id;
SET offset = 1;
SET no_more_rows = TRUE;
select FOUND_ROWS() into num_rows;
OPEN ucur;
uloop: LOOP
FETCH ucur
if counter >= num_rows then
no_more_rows = False;
endif
INTO title_urlvalue;
IF no_more_rows THEN
CLOSE ucur;
LEAVE uloop;
END IF;
update title_urlvalue = Concat(title_urlvalue,offset);
SET offset = offset + 1;
SET counter = counter + 1;
END LOOP uloop;
close ucur;
With User-Defined Variables
SET #counter:=0;
SET #title_url:='';
UPDATE phpfox_photo n
JOIN (SELECT title_url, MIN(photo_id) min_id
FROM phpfox_photo
GROUP BY title_url
HAVING COUNT(*) > 1) d
ON n.title_url = d.title_url AND n.photo_id <> d.min_id
SET n.title_url = IF(n.title_url <> #title_url, CONCAT(#title_url:=n.title_url, #counter:=1), CONCAT(n.title_url, #counter:=#counter+1));
Maybe you can use modulo to produce numbering, like this (SQLite example, but should be similar in mysql):
SELECT *, (rowid % (SELECT COUNT(*) FROM table as t WHERE t.name = table.name ) ) FROM table ORDER BY name
All you need is to translate rowid and modulo function, both availible in mysql.
Then you can CONCAT results as you desire.
UPDATE phpfox_photo n
JOIN
(SELECT title_url,
MIN(photo_id) min_id
FROM phpfox_photo
GROUP BY title_url
HAVING COUNT(*) > 1
)
d
ON n.title_url = d.title_url
AND n.photo_id <> d.min_id
SET n.title_url =
CASE
WHEN <last char is int>
THEN <replace last char with incremented last char>
ELSE <string + 1>
END