I have table like this
name | personal_number
-----------------------------------------
Jon | 222
Alex | 555
Jon | 222
Jimmy | 999
I need get every name, which personal_number repeates in table more than 1, that is result must be:
Jon
Jon
So, Variant 1):
SELECT name FROM mytable WHERE personal_number IN (
SELECT personal_number FROM mytable GROUP BY personal_number
HAVING COUNT(*) > 1
)
Variant 2):
SELECT personal_number FROM mytable GROUP BY personal_number
HAVING COUNT(*) > 1
)
Then, using php, retrieved personal_numbers join as string (soemthing like this '222', '222' ) and run other query
SELECT name FROM mytable WHERE personal_number IN( here joined string )
Variant 2 works approximately 10 times faster, than variant 1, this is surprise for me, I was thinking that one query will be faster, but...
(In table is 500 000 rows, column personal_number not indexed)
So, what you mean about cases like this? why variant 2 is many faster than variant 1 ?
It seems that subqueries are very slow as mentioned in this article http://www.mysqlperformanceblog.com/2010/10/25/mysql-limitations-part-3-subqueries.
You should try to avoid having subqueries and use joining instead.
First query has heavy subquery. You must avoid this.
The best solution for your problem is only one query:
SELECT name FROM mytable GROUP BY personal_number HAVING COUNT(*) > 1;
This query will return you each repeated name only once. If you want to display the name of the duplicate as many times as they met you must use next query:
SELECT name, COUNT(*) AS count FROM mytable GROUP BY personal_number HAVING COUNT(*) > 1;
And then in PHP do something like this:
foreach ($rows as $row) {
for ($i = 0; $i++; $i < $row['count']) {
echo $row['name'] . "\n";
}
}
Since indexing is not done so the 1 is slow,as it has to match personal_numbers from selected personal_numbers. If indexing is done it consumes less time than earlier.
Variant 2 is a direct query hence its faster.
This should be quicker:
SELECT name FROM mytable join (
SELECT personal_number FROM mytable GROUP BY personal_number
HAVING COUNT(*) > 1
)a using (personel_number)
Edit: If this is faster than variant 1, then it means at variant 1 mysql reproduces the inner table for each record again and again.
Related
I searched a lot and tried many queries but not getting satisfied answer. So like to ask.
I am looking for last 5 records from mysql table if having same value otherwise not.
Something like if col_n is having same value x from last 5 records then count otherwise not. But I am not able to figure out how to write query for this ?
SELECT count(col_n)
from track if(last five col_n = 'ok')
WHERE col_a = 'value1' AND col_b = 'value2'
enter mysql table records
Please try this:
SELECT p.*
FROM
( SELECT *
FROM demo ORDER by id DESC
LIMIT 5
) AS p
JOIN
( SELECT COUNT(*) AS cnt
FROM
( SELECT 1
FROM demo
LIMIT 5
) AS tmp
) AS c
ON c.cnt = 5 WHERE p.name='x'
This question already has answers here:
Implement paging (skip / take) functionality with this query
(6 answers)
Closed 1 year ago.
I have this query with MySQL:
select * from table1 LIMIT 10,20
How can I do this with SQL Server?
Starting SQL SERVER 2005, you can do this...
USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
FROM Sales.SalesOrderHeader
)
SELECT *
FROM OrderedOrders
WHERE RowNumber BETWEEN 10 AND 20;
or something like this for 2000 and below versions...
SELECT TOP 10 * FROM (SELECT TOP 20 FROM Table ORDER BY Id) ORDER BY Id DESC
Starting with SQL SERVER 2012, you can use the OFFSET FETCH Clause:
USE AdventureWorks;
GO
SELECT SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
ORDER BY SalesOrderID
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
GO
http://msdn.microsoft.com/en-us/library/ms188385(v=sql.110).aspx
This may not work correctly when the order by is not unique.
If the query is modified to ORDER BY OrderDate, the result set returned is not as expected.
This is how I limit the results in MS SQL Server 2012:
SELECT *
FROM table1
ORDER BY columnName
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
NOTE: OFFSET can only be used with or in tandem to ORDER BY.
To explain the code line OFFSET xx ROWS FETCH NEXT yy ROW ONLY
The xx is the record/row number you want to start pulling from in the table, i.e: If there are 40 records in table 1, the code above will start pulling from row 10.
The yy is the number of records/rows you want to pull from the table.
To build on the previous example: If table 1 has 40 records and you began pulling from row 10 and grab the NEXT set of 10 (yy).
That would mean, the code above will pull the records from table 1 starting at row 10 and ending at 20. Thus pulling rows 10 - 20.
Check out the link for more info on OFFSET
This is almost a duplicate of a question I asked in October:
Emulate MySQL LIMIT clause in Microsoft SQL Server 2000
If you're using Microsoft SQL Server 2000, there is no good solution. Most people have to resort to capturing the result of the query in a temporary table with a IDENTITY primary key. Then query against the primary key column using a BETWEEN condition.
If you're using Microsoft SQL Server 2005 or later, you have a ROW_NUMBER() function, so you can get the same result but avoid the temporary table.
SELECT t1.*
FROM (
SELECT ROW_NUMBER OVER(ORDER BY id) AS row, t1.*
FROM ( ...original SQL query... ) t1
) t2
WHERE t2.row BETWEEN #offset+1 AND #offset+#count;
You can also write this as a common table expression as shown in #Leon Tayson's answer.
SELECT *
FROM (
SELECT TOP 20
t.*, ROW_NUMBER() OVER (ORDER BY field1) AS rn
FROM table1 t
ORDER BY
field1
) t
WHERE rn > 10
Syntactically MySQL LIMIT query is something like this:
SELECT * FROM table LIMIT OFFSET, ROW_COUNT
This can be translated into Microsoft SQL Server like
SELECT * FROM
(
SELECT TOP #{OFFSET+ROW_COUNT} *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS rnum
FROM table
) a
WHERE rnum > OFFSET
Now your query select * from table1 LIMIT 10,20 will be like this:
SELECT * FROM
(
SELECT TOP 30 *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS rnum
FROM table1
) a
WHERE rnum > 10
SELECT TOP 10 * FROM table;
Is the same as
SELECT * FROM table LIMIT 0,10;
Here's an article about implementing Limit in MsSQL Its a nice read, specially the comments.
This is one of the reasons I try to avoid using MS Server... but anyway. Sometimes you just don't have an option (yei! and I have to use an outdated version!!).
My suggestion is to create a virtual table:
From:
SELECT * FROM table
To:
CREATE VIEW v_table AS
SELECT ROW_NUMBER() OVER (ORDER BY table_key) AS row,* FROM table
Then just query:
SELECT * FROM v_table WHERE row BETWEEN 10 AND 20
If fields are added, or removed, "row" is updated automatically.
The main problem with this option is that ORDER BY is fixed. So if you want a different order, you would have to create another view.
UPDATE
There is another problem with this approach: if you try to filter your data, it won't work as expected. For example, if you do:
SELECT * FROM v_table WHERE field = 'test' AND row BETWEEN 10 AND 20
WHERE becomes limited to those data which are in the rows between 10 and 20 (instead of searching the whole dataset and limiting the output).
In SQL there's no LIMIT keyword exists. If you only need a limited number of rows you should use a TOP keyword which is similar to a LIMIT.
Must try. In below query, you can see group by, order by, Skip rows, and limit rows.
select emp_no , sum(salary_amount) from emp_salary
Group by emp_no
ORDER BY emp_no
OFFSET 5 ROWS -- Skip first 5
FETCH NEXT 10 ROWS ONLY; -- limit to retrieve next 10 row after skiping rows
Easy way
MYSQL:
SELECT 'filds' FROM 'table' WHERE 'where' LIMIT 'offset','per_page'
MSSQL:
SELECT 'filds' FROM 'table' WHERE 'where' ORDER BY 'any' OFFSET 'offset'
ROWS FETCH NEXT 'per_page' ROWS ONLY
ORDER BY is mandatory
This is a multi step approach that will work in SQL2000.
-- Create a temp table to hold the data
CREATE TABLE #foo(rowID int identity(1, 1), myOtherColumns)
INSERT INTO #foo (myColumns) SELECT myData order By MyCriteria
Select * FROM #foo where rowID > 10
SELECT
*
FROM
(
SELECT
top 20 -- ($a) number of records to show
*
FROM
(
SELECT
top 29 -- ($b) last record position
*
FROM
table -- replace this for table name (i.e. "Customer")
ORDER BY
2 ASC
) AS tbl1
ORDER BY
2 DESC
) AS tbl2
ORDER BY
2 ASC;
-- Examples:
-- Show 5 records from position 5:
-- $a = 5;
-- $b = (5 + 5) - 1
-- $b = 9;
-- Show 10 records from position 4:
-- $a = 10;
-- $b = (10 + 4) - 1
-- $b = 13;
-- To calculate $b:
-- $b = ($a + position) - 1
-- For the present exercise we need to:
-- Show 20 records from position 10:
-- $a = 20;
-- $b = (20 + 10) - 1
-- $b = 29;
If your ID is unique identifier type or your id in table is not sorted you must do like this below.
select * from
(select ROW_NUMBER() OVER (ORDER BY (select 0)) AS RowNumber,* from table1) a
where a.RowNumber between 2 and 5
The code will be
select * from limit 2,5
better use this in MSSQLExpress 2017.
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) as [Count], * FROM table1
) as a
WHERE [Count] BETWEEN 10 and 20;
--Giving a Column [Count] and assigning every row a unique counting without ordering something then re select again where you can provide your limits.. :)
One of the possible way to get result as below , hope this will help.
declare #start int
declare #end int
SET #start = '5000'; -- 0 , 5000 ,
SET #end = '10000'; -- 5001, 10001
SELECT * FROM (
SELECT TABLE_NAME,TABLE_TYPE, ROW_NUMBER() OVER (ORDER BY TABLE_NAME) as row FROM information_schema.tables
) a WHERE a.row > #start and a.row <= #end
If i remember correctly (it's been a while since i dabbed with SQL Server) you may be able to use something like this: (2005 and up)
SELECT
*
,ROW_NUMBER() OVER(ORDER BY SomeFields) AS [RowNum]
FROM SomeTable
WHERE RowNum BETWEEN 10 AND 20
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)
I'm very new with SQL and need assistance on how I can accomplish this task using the correct query.
I have 2 tables that I need to use. Table "TB1" has:
id Name
1 bob
2 blow
3 joe
table "TB2" has:
compid property
1 bob
2 blow
I am trying to get which compid is missing in "TB2" and insert it from "TB1"
the query I am doing is:
SELECT id, name from TB1, TB2 where id <> compid
what I get is 2 ouputs of Id 1, and 2, and 3 outputs from id 3. by using php:
for($i=0;$i <= mysql_num_rows($comp)-1; $i++)
{
echo mysql_result($comp, $i, 0)."<br>";
}
and I expected the ouput 3 but instead got this:
1
1
2
2
3
3
3
I understand its comparing all the rows within the table but is there a way to achieve what I am looking for?
Thanks for your time.
You are performing an implicit Cartesian JOIN which results in every row against every other row. You need to specify what attribute JOINs the two tables.
Using implicit syntax (not recommended):
SELECT id, name
FROM TB1, TB2
WHERE id <> compid
AND TB1.Name = TB2.property <-- Column join
Using explicit syntax:
SELECT id, name
FROM TB1
JOIN TB2
ON TB2.property = TB1.Name <-- Column join
WHERE id <> compid
To accomplish your goal you would need something along the lines of:
SELECT TB1.id, TB1.name
FROM TB1
LEFT JOIN TB2
ON TB2.property = TB1.Name
WHERE TB2.compid IS NULL
See it in action
It's best practice to always alias the columns you select to prevent ambiguity.
To select it you can do:
SELECT *
FROM TB1
WHERE id NOT IN (
SELECT compid
FROM TB2
);
I have a table:
ID int
category int
quantity int
timestamp timestamp
I want to SELECT id="id#" and return a 'position' which is the sum of quantity for each row that has an earlier timestamp in the same category.
Thanks for your pointers!
Try this:
SELECT sum(quantity) FROM TABLE WHERE category='...' AND timestamp < some_timestamp
If you want to select by id:
SELECT sum(quantity) FROM mytable
WHERE category IN (SELECT category FROM mytable WHERE id=some_id)
AND timestamp <= some_timestamp
UPDATE
To use the timestamp from the row itself, you could do something like:
SELECT sum(quantity) FROM mytable
WHERE category IN (SELECT category FROM mytable WHERE id=some_id)
AND timestamp <= (SELECT timestamp FROM mytable WHERE id=some_id)
..or use a self-join as in the Adam's answer (maybe it wasn't an overkill, after all.. :))
UPDATE - Alternate solution
This seems to work fine too..
SELECT t1.id, sum(t2.quantity), t1.timestamp, t1.category
FROM mytable t1 INNER JOIN mytable AS t2 ON t1.category=t2.category
WHERE t1.id=some_id_here AND t2.timestamp <= t1.timestamp;
UPDATE
Changed < into <=, else the quantity for the selected item will not be counted!
You could join your table again with a subquery filtering on your criteria of same category and lesser timestamp.
Here's an example (this was done is sqlite, but it should work in mysql):
select
ID,
sum(foo2.quantity)
from
foo
left join (
select category, quantity, timestamp from foo
) as foo2 on (
foo.category = foo2.category
and foo.timestamp > foo2.timestamp
)
group by
foo.ID
Assuming your data looks like this:
ID|category|quantity|timestamp
1 |foo |2 |2011-01-01
2 |foo |1 |2011-01-02
3 |foo |4 |2011-01-03
4 |bar |4 |2011-01-03
You'll get a result like this:
ID|sum(foo2.quantity)
1 |null
2 |2
3 |3
4 |null
Note: the nulls are because there is no data for those categories prior to the given record.
Note 2: Not sure how performant this will be, but it should get you the data you are looking for.
Update
After re-reading your question, this looks to be a bit overkill... I didn't realize you were passing the id in, either way you could still use this and add a where id = "$id" to restrict it to the id are looking at... hope this helps.
SELECT * FROM table WHERE ID=(SELECT SUM(quantity) WHERE timestamp < some_timestamp AND category = (SELECT category FROM table WHERE ID='given_ID'))
If I understood correctly, this would give you the line which ID is the sum of the quantity of all the lines that have an earlier timestamp and are in the same category as the provided ID.
I didn't test it, but something along those lines should work.
If I understand your question correctly, something like this should do the trick.
$r = mysql_query("SELECT * FROM table WHERE ID='$id'");
$r = mysql_fetch_array($r);
$cat = $r['category'];
$time = $r['timestamp'];
$r = mysql_query("SELECT SUM(quantity) FROM table WHERE category='$cat' AND timestamp <= '$time'");
The first 4 lines retrieve your original id and find the category id and timestamp. Then we run a new query to get all the items in the category older than our id. Because we use <= for the timestamp, it will include our selected id's quantity. If we want to exclude our id's quantity, we would use just <
EDIT: Reading some of the other answers, you could use SUM and skip the while loop... you learn something new every day. Answer changed to reflect this.