I'm trying to output the number of distinct results from a row where two other tables factor into the equation, but I'm not sure how to make this work. Here's what I have. I have counted the total times which a word appears in the database.
$total = "SELECT word, count(word) total FROM Table WHERE Word = 'Apples'";
total = 9. Ok, good. That was easy.
+-------------+--------------+--------------+--------------+
| BookID (INT)| chapter (INT)| page (INT) | word (VCHAR) |
+-------------+--------------+--------------+--------------+
| 40 | 1 | 8 | Apples |
| 40 | 1 | 8 | Apples |
| 40 | 1 | 15 | Apples |
| 40 | 4 | 23 | Apples |
| 50 | 3 | 15 | Apples |
| 50 | 6 | 15 | Apples |
| 51 | 13 | 1 | Apples |
| 52 | 2 | 3 | Apples |
| 60 | 8 | 1 | Apples |
+-------------------------------------------+--------------+
Now, here's where I'm needing some assistance. I want to find the number of times the word is used on a DISTINCT page in each chapter of each book. So, based on the table above, I am expecting the total to be 8. I'm close with this code, but I can't find the next step.
$pages = "SELECT COUNT(DISTINCT page) AS num FROM Table WHERE word = 'Apples' GROUP BY BookID, chapter, page";
This gives me:
1
1
1
1
1
1
1
1
So, I'm getting the correct number. 8 rows. Now I just need to add those up and output them as a single number. 8. I've looked into SUM, but that doesn't seem to work (if I'm mistaken, please show me how I should include it.)
You may simply take the sum of your current query as a subquery:
SELECT SUM(num) AS total
FROM
(
SELECT COUNT(DISTINCT page) AS num
FROM yourTable
WHERE word = 'Apples'
GROUP BY BookID, chapter, page
) t;
Related
I have a table of food items. They have a "Position" field that represents the order they should appear in on a list (listID is the list they are on, we don't want to re-order items on another list).
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 1 |
| 3 | 1 | bacon | 2 |
| 4 | 1 | apples | 3 |
| 5 | 1 | pears | 4 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
I want to be able to say "Move Pears to before Chips" which involves setting the position of Pears to position 1, and then incrementing all the positions inbetween by 1. so that my resulting Table look like this...
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
So that all I need to do is SELECT name FROM mytable WHERE listID = 1 ORDER BY position and I'll get all my food in the right order.
Is it possible to do this with a single query? Keep in mind that a record might be moving up or down in the list, and that the table contains records for multiple lists, so we need to isolate the listID.
My knowledge of SQL is pretty limited so right now the only way I know of to do this is to SELECT id, position FROM mytable WHERE listID = 1 AND position BETWEEN 1 AND 5 then I can use Javascript (node.js) to change position 5 to 1, and increment all others +1. Then UPDATE all the records I just changed.
It's just that anytime I try to read up on SQL stuff everyone keeps saying to avoid multiple queries and avoid doing syncronous coding and stuff like that.
Thanks
This calls for a complex query that updates many records. But a small change to your data can change things so that it can be achieved with a simple query that modifies just one record.
UPDATE my_table set position = position*10;
In the old days, the BASIC programming language on many systems had line numbers, it encouraged spagetti code. Instead of functions many people wrote GOTO line_number. Real trouble arose if you numbered the lines sequentially and had to add or delete a few lines. How did people get around it? By increment lines by 10! That's what we are doing here.
So you want pears to be the second item?
UPDATE my_table set position = 15 WHERE listId=1 AND name = 'Pears'
Worried that eventually gaps between the items will disappear after multiple reordering? No fear just do
UPDATE my_table set position = position*10;
From time to time.
I do not think this can be conveniently done in less than two queries, which is OK, there should be as few queries as possible, but not at any cost. The two queries would be like (based on what you write yourself)
UPDATE mytable SET position = 1 WHERE listID = 1 AND name = 'pears';
UPDATE mytable SET position = position + 1 WHERE listID = 1 AND position BETWEEN 2 AND 4;
I've mostly figured out my problem. So I've decided to put an answer here incase anyone finds it helpful.
I can make use of a CASE statement in SQL. Also by using Javascript beforehand to build my SQL query I can change multiple records.
This builds my SQL query:
var sql;
var incrementDirection = (startPos > endPos)? 1 : -1;
sql = "UPDATE mytable SET position = CASE WHEN position = "+startPos+" THEN "+endPos;
for(var i=endPos; i!=startPos; i+=incrementDirection){
sql += " WHEN position = "+i+" THEN "+(i+incrementDirection);
}
sql += " ELSE position END WHERE listID = "+listID;
If I want to move Pears to before Chips. I can set:
startPos = 4;
endPos = 1;
listID = 1;
My code will produce an SQL statement that looks like:
UPDATE mytable
SET position = CASE
WHEN position = 4 THEN 1
WHEN position = 1 THEN 2
WHEN position = 2 THEN 3
WHEN position = 3 THEN 4
ELSE position
END
WHERE listID = 1
I run that code and my final table will look like:
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
After that, all I have to do is run SELECT name FROM mytable WHERE listID = 1 ORDER BY position and the output will be as follows::
cheese
pears
chips
bacon
apples
pie
I have a table with multiple rows for each customer and and a visit_date. The visit date can be null as well.
My tables are as below:
customers:
id | name | email
1 | John Doe1 | a.b#gmail.com
2 | John Doe2 | b.c#gmail.com
3 | John Doe3 | x.y#gmail.com
store_customers
id | customer_id | store_id | email_optedin | visit_date
1 | 1 | 1 | 1 | 2015-11-30
2 | 1 | 2 | 1 | 2016-05-08
3 | 1 | 3 | 1 | null
4 | 2 | 1 | 1 | 2015-04-30
5 | 2 | 2 | 1 | 2015-08-40
6 | 2 | 3 | 1 | 2015-12-12
7 | 3 | 1 | 1 | null
8 | 3 | 2 | 1 | null
9 | 3 | 3 | 1 | null
I am trying to retrieve customers who either have not had a visit to the any of the three stores or have not visited since a specified date (e.g. 2016-04-15).
I am expecting customers 2 and 3 but not 1.
I tried this query:
select distinct * from customers
inner join store_customers on store_customers.customer_id = customers.id
where customers.email != '' and
store_customer.store_id in (1,2,3) and customers.emailStatus not in ('Unverified','Bounced','Spammed')
and
(
store_customer.email_optedin = 1
and max(store_customers.visit_date) <= '2016-04-15'
or account_customer.visit_date is null
);
This does not work. I somehow need to, for the set of store ids), I need to select customers who have either not had any visit (all nulls for visit date for the specified stores) or the if one or more visit dates are available then compare the max date to the specified date.
I found similar questions but none of the answers has worked for me, mainly because of the requirement of selecting either those customers who have no visit or if they do atleast one, then to compare the latest visit date from the set of stores in the joined table.
I am trying to do this all in one query but if that is not possible then I can break it up as well. I also do not want to change the order of joins because there are many other things added on to this query and changing the order of joins may become a problem.
I really appreciate any help that can be provided.
Regards,
Waqar
Try this query
SELECT
customers.id,
customers.name,
MAX(store_customers.visit_date)
FROM
customers LEFT JOIN store_customers on customers.id = store_customers.customer_id
GROUP BY customers.id,customers.name
HAVING MAX(store_customers.visit_date) < '2016-04-15' OR MAX(store_customers.visit_date) IS NULL
i already done everything to remove this duplicity on the database
On selecting a checkbox on the sectio "Bairros" i utilized as Array
for($m=0; $m<count($_POST["bairros"]); $m++){// LOOP 1
$pesquisar=($_POST["bairros"][$m]);
//Copy bairros(Array) and esporte (POST)
$query = "SELECT DISTINCT * FROM cadastro WHERE
(esporte1 = '".$_POST["esportes"]."' OR
esporte2 = '".$_POST["esportes"]."' OR
esporte3 = '".$_POST["esportes"]."' OR
esporte4 = '".$_POST["esportes"]."')
AND
(bairro1 = '".$pesquisar."' OR
bairro2 = '".$pesquisar."' OR
bairro3 = '".$pesquisar."' OR
bairro4 = '".$pesquisar."')
AND
ativarAparecer='sim' ORDER BY nomeCompleto ASC LIMIT 20";
$esporte= new consultar();
$esporte->executa($query);
//Loops
for($l=0; $l<$esporte->nrw; $l++){ //LOOP 2
echo $esporte->data["nomeCompleto"]."<br />";
$esporte->proximo();
} //close LOOP2
} //close LOOP1
Detail: this function object oriented, I believe that i'm doing something wrong at SQL or MYSQL, perhaps something is missing there.
SELECT DISTINCT *
Stop There. DISTINCT * can do what? Duplicate of what? it cant do that. Give it a field name to see unique values.
For example
SELECT DISTINCT nomeCompleto
Let's break this down. The DISTINCT clause will return unique sets based on the selected columns.
Let's say you have a table:
a | b | c
=========
1 | 2 | 3
1 | 1 | 3
1 | 2 | 4
Now if you SELECT DISTINCT a FROM table, you would get:
1
but if you SELECT DISTINCT a, b FROM table, you would get:
a | b
=====
1 | 2
1 | 1
That's because {1,2} is different from {1,1}, even though the a column is the same between those two sets.
Obviously, doing SELECT DISTINCT * FROM table would give you the original table because it uses all three columns as a "composition" of the unique set. If we amended the table to look like this:
a | b | c
=========
1 | 2 | 3
1 | 1 | 3
1 | 2 | 4
1 | 2 | 3
Then your result of SELECT DISTINCT * FROM table would give:
a | b | c
=========
1 | 2 | 3
1 | 1 | 3
1 | 2 | 4
because of the duplicate result set of {1, 2, 3}. However, since most tables have an auto-incrementing identifier as the primary key, there is almost always no difference between SELECT * and SELECT DISTINCT *.
Perhaps you're looking to GROUP BY a certain column?
How would I be using GROUP this in my script? Column that there are several equal records are this bairro, bairro2, bairro3, bairro4. Inside it is in numbers
bairro1 | bairro2 | bairro3 | bairro4
14 | 14 | 15 | 27
34 | 15 | 14 | 30
27 | 45 | 12 | 14
I have the following sql which selects the most recurring row first based on the column "reported"
$datan = mysql_query("
SELECT *, COUNT(reported) AS ct
FROM profile_reports
WHERE open = '1'
GROUP BY reported
ORDER BY ct DESC
LIMIT 1
") or die(mysql_error());
I want my sql to also check which 'reporter' (each is a number associated with a user) has the best percentage of useful reports, which is determined this way:
((raction > 0 AND raction < 99 AND open = '0' AND reporter = 'reporter') / (reporter = 'reporter' AND open = '0')) * 100
...and show the rows with highest percentage first. It's a little tricky because no initial reporter is set.
Here's a sample table:
+----+----------+----------+-------+----------+
| id | reporter | reported | open | raction |
+----+----------+----------+-------+----------+
| 1 | 24 | 26 | 0 | 3 |
| 2 | 24 | 23 | 0 | 0 |
| 3 | 24 | 29 | 1 | |
| 4 | 12 | 29 | 0 | 4 |
| 5 | 12 | 29 | 1 | |
| 6 | 24 | 21 | 1 | 0 |
+----+----------+----------+-------+----------+
I want it to see that there are more reports about user 29(column: reported), then check which reporting user(column: reporter) has the best percentage (based on the line of code above), in this case user 12, and display their report
Its actually pretty easy in just take the sums of your conditions and divide. In order to get the "Reported" correctly you'll need to use an inline view to find the highest report.
SELECT pr.*,
( Sum(pr.raction > 0
AND pr.raction < 99
AND pr.open = '0'
AND pr.reported = t.reported) / Sum(pr.reported = t.reported
AND pr.open = '0') ) * 100 AS
usefull
FROM profile_reports pr,
(SELECT reported
FROM profile_reports
WHERE open = '1'
GROUP BY reported
ORDER BY Count(reported) DESC
LIMIT 1) t
GROUP BY reporter
ORDER BY usefull DESC
LIMIT 1
demo
Output
| ID | REPORTER | REPORTED | OPEN | RACTION | USEFULL |
-------------------------------------------------------
| 4 | 12 | 29 | 0 | 4 | 100 |
I haven't done everything for you. You will have to decide what to do if the divisor is zero
Note in just about everything but MySQL you would need to use CASE
SUM ( CASE WHEN raction > 0 AND .... THEN 1 ELSE 0 END) / ....
I have two tables one that contains a huge list of items and another that trading for those items.
Here are examples tables:
The main table
| ID | TITLE | STATUS | TRADE |
-------------------------------
| 1 | test1 | 1 | 1 |
| 2 | test2 | 1 | 1 |
| 3 | test3 | 1 | 0 |
| 4 | test4 | 0 | 1 |
The trade table
| ID | TRADER | ITEM | URL |
------------------------------------------------------
| 1 | 2 | 1 | HTTP://www.test.com/itemOne |
| 2 | 5 | 3 | HTTP://www.test.com/itemThree |
| 3 | 5 | 4 | HTTP://www.test.com/itemFour |
Say I want to have a list of all the items that are not being traded by trader 5 and have a status of 1. So when trader 5 comes to the site they will be able to select the remaining items to trade.
Here is what I have tried:
$sql = "SELECT m.id, m.title
FROM main AS m, trade AS t
WHERE m.trade >= 1 && m.status = 1 &&
t.trader <>". mysql_real_escape_string($traderID);
This code just doesn't work. Any ideas on this?
It is not clear to me what column in Trades is an FK to Main. Below, I have assumed it is the Item column:
select m.id, m.title
from Main m
where not exists (
select *
from trade
where m.id = item
and trader = 5
)
and m.status = 1
Try this:
SELECT id, title FROM main
WHERE status = 1 AND id NOT IN
(SELECT item FROM trade WHERE trader = 5);
This will grab a list of every title in main with a status of 1, but limit the items based on a subquery which gets a list of ids already traded by trader 5 (i.e. items "not in" the list of items returned as having been traded by trader 5).
I'll leave it to you to update the query to be parameterized as needed.
Note that I'm assuming that item in trade is a foreign key to the id field in main, since you didn't specify it.