Is it possible using pure SQL to add multiple rows into a table based on a value in a given field?
I have table named products, for simplicity this is an example:
id, views
1, 4
For that row, I want to insert 4 rows into another table. How can I do this?
I've written a PHP script to do this but would prefer a SQL version but I have no idea how to even start.
In order to do this, you need a way to generate values. If you have just a handful, you can do this manually:
insert into table(id)
select p.id, n.n as which
from products p join
(select 1 as n union all select 2 union all select 3 union all select 4 union all select 5
) n
on p.views <= n.n;
If you have a table of numbers you can use that as well.
Related
Sorry if the title is misleading, I don't really know the terminology for what I want to accomplish. But let's consider this table:
CREATE TABLE entries (
id INT NOT NULL,
number INT NOT NULL
);
Let's say it contains four numbers associated with each id, like this:
id number
1 0
1 9
1 17
1 11
2 5
2 8
2 9
2 0
.
.
.
Is it possible, with a SQL-query only, to count the numbers of matches for any two given numbers (tuples) associated with a id?
Let's say I want to count the number of occurrences of number 0 and 9 that is associated with a unique id. In the sample data above 0 and 9 does occur two times (one time where id=1 and one time where id=2). I can't think of how to write a SQL-query that solves this. Is it possible? Maybe my table structure is wrong, but that's how my data is organized right now.
I have tried sub-queries, unions, joins and everything else, but haven't found a way yet.
You can use GROUP BY and HAVING clauses:
SELECT COUNT(s.id)
FROM(
SELECT t.id
FROM YourTable t
WHERE t.number in(0,9)
GROUP BY t.id
HAVING COUNT(distinct t.number) = 2) s
Or with EXISTS():
SELECT COUNT(distinct t.id)
FROM YourTable t
WHERE EXISTS(SELECT 1 FROM YourTable s
WHERE t.id = s.id and s.id IN(0,9)
HAVING COUNT(distinct s.number) = 2)
Problem Statement: I need my result set to include records that would not naturally return because they are NULL.
I'm going to put some simplified code here since my code seems to be too long.
Table Scores has Company_type, Company, Score, Project_ID
Select Score, Count(Project_ID)
FROM Scores
WHERE company_type= :company_type
GROUP BY Score
Results in the following:
Score Projects
5 95
4 94
3 215
2 51
1 155
Everything is working fine until I apply a condition to company_type that does not include results in one of the 5 score categories. When this happens, I don't have 5 rows in my result set any more.
It displays like this:
Score Projects
5 5
3 6
1 3
I'd like it to display like this:
Score Projects
5 5
4 0
3 6
2 0
1 3
I need the results to always display 5 rows. (Scores = 1-5)
I tried one of the approaches below by Spencer7593. My simplified query now looks like this:
SELECT i.score AS Score, IFNULL(count(*), 0) AS Projects
FROM (SELECT 5 AS score
UNION ALL
SELECT 4
UNION ALL
SELECT 3
UNION ALL
SELECT 2
UNION ALL
SELECT 1) i
LEFT JOIN Scores ON Scores.score = i.score
GROUP BY Score
ORDER BY i.score DESC
And gives the following results, which is accurate except that the rows with 1 in Projects should actually be 0 because they are derived by the "i". There are no projects with a score of 5 or 2.
Score Projects
5 1
4 5
3 6
2 1
1 3
Solved! I just needed to adjust my count to specifically look at the project count - count(project) rather than count(*). This returned the expected results.
If you always want your query to return 5 rows, with Score values of 5,4,3,2,1... you'll need a rowsource that supplies those Score values.
One approach would be to use a simple query to return those fixed values, e.g.
SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
Then use that query as inline view, and do an outer join operation to the results from your current query
SELECT i.score AS `Score`
, IFNULL(q.projects,0) AS `Projects`
FROM ( SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
) i
LEFT
JOIN (
-- the current query with "missing" Score rows goes here
-- for completeness of this example, without the query
-- we emulate that result with a different query
SELECT 5 AS score, 95 AS projects
UNION ALL SELECT 3, 215
UNION ALL SELECT 1, 155
) q
ON q.score = i.score
ORDER BY i.score DESC
It doesn't have to be the view query in this example. But there does need to be a rowsource that the rows can be returned from. You could, for example, have a simple table that contains those five rows, with those five score values.
This is just an example approach for the general approach. It might be possible to modify your existing query to return the rows you want. But without seeing the query, the schema, and example data, we can't tell.
FOLLOWUP
Based on the edit to the question, showing an example of the current query.
If we are guaranteed that the five values of Score will always appear in the Scores table, we could do conditional aggregation, writing a query like this:
SELECT s.score
, COUNT(IF(s.company_type = :company_type,s.project_id,NULL)) AS projects
FROM Scores s
GROUP BY s.score
ORDER BY s.score DESC
Note that this will require a scan of all the rows, so it may not perform as well. The "trick" is the IF function, which returns a NULL value in place of project_id, when the row would have been excluded by the WHERE clause.)
If we are guaranteed that project_id is non-NULL, we could use a more terse MySQL shorthand expression to achieve an equivalent result...
, IFNULL(SUM(s.company_type = :company_type),0) AS projects
This works because MySQL returns 1 when the comparison is TRUE, and otherwisee returns 0 or NULL.
Try something like this:
select distinct score
from (
select distinct score from scores
) s
left outer join (
Select Score, Count(Project_ID) cnt
FROM Scores
WHERE company_type= :company_type
) x
on s.score = x.score
Your posted query would not work without a group by statement. However, even there, if you don't have those particular scores for that company type, it wouldn't work either.
One option is to use an outer join. That would require a little more work though.
Here's another option using conditional aggregation:
select Score, sum(company_type=:company_type)
from Scores
group by Score
In a mysql table with "R rows" I want to select the next "m rows" after "nth row" in such a way that if n+m>R it returns R-n rows from the end of table and m+n-R rows from the beginning of the table.
e.g in this table:
id firstname
1 john
2 robert
3 bob
4 adam
5 david
I want to get the next 4 rows after row number 3 (bob), in this fashion:
4 adam
5 david
1 john
2 robert
I have searched a lot and found that the following query just returns the last 2 rows.
SELECT * FROM table LIMIT 4 OFFSET 3;
I know that I can implement this specific query using php and bunch of conditional statements but I am curious to know whether it has been implemented in mysql or not?
One approach is to use union all in a subquery. This allows you to "duplicate" the table, with a newly calculated id at the end of the table:
select t.*
from ((select t.*, id as newid from table t) union all
(select t.*, id + cnt as newid
from table t cross join
(select count(*) as cnt from table) cnt
)
) t
order by newid
limit 4 offset 3;
For small tables, this should be fine. For larger tables, you probably don't want to do this because the MySQL materializes the subquery -- adding overhead to the processing of the query.
For simplicity, suppose I have a table transactions with id as the primary key. Currently there are only 10 rows in the table with id from 1 to 10.
I have a list of ids: {9,10,11,12}. This list is not stored in the database.
I want to query the database for the ids not in the transactions table. In the case above I want to get 11, 12.
What's the best way to way to write this query?
Currently I just query SELECT * FROM transactions WHERE id IN (9,10,11,12). And do my intersection in code. I'm wondering is I can do it all in one step in SQL.
You can do this with a subquery containing the ids. Here is one way:
select ids.id
from (select 9 as id union all select 10 union all select 11 union all select 12
) ids
where not exists (select 1 from transactions t where t.id = ids.id);
Returning rows from a table called transactions seems inefficient -- way too much data going back and forth for what you need. (Although you only have 10 rows, so this isn't a big deal with your data size.)
An alternative approach would be to use the EXCEPT clause. For example;
select 9 as id union all select 10 union all select 11 union all select 12
except select id from transactions
I'm using the UNION operator to select results from two different tables. I want results from the first table result to come before those from the second table.
For example: I have the tables customer_coupons and segment_coupons. Both tables have a column named coupon_id. When I run a query involving a UNION of these two tables, it returns the correct records, but they are not the order I want: It gives me the coupon_ids of both tables mixed in ascending order, but I want to show ALL coupon_ids of the first table and then ALL coupon_ids of the second table.
Here's the query as it currently exists:
SELECT coupon_id
FROM customer_coupons
UNION
SELECT coupon_id
FROM segment_coupons;
How can I change this so that all results from the first half of the query come before all results of the second half?
Put in a fixed table-identifying field:
(SELECT 1 AS source_table, coupon_id
FROM customer_coupons)
UNION ALL
(SELECT 2 AS sourcE_table, coupon_id
FROM segment_coupons)
ORDER BY source_table, coupon_id
Note the brackets around the individual queries. This forces MySQL to apply the order by to the result of the union, not to the 2 sub-query.
SELECT * FROM (
SELECT coupon_id, 1 as myorder
FROM customer_coupons
UNION
SELECT coupon_id 2 as myorder
FROM segment_coupons)
Order by myorder