SQL COUNT(*) in one table including zero values - mysql

I have SQL table (With more columns, but it is not important)
CREATE TABLE `test_results` (
`id` int AUTO_INCREMENT,
`date_time` datetime,
`altpn` varchar(60),
`error1` int,
`error2` int,
`error3` int,
PRIMARY KEY (`id`)
);
Data can be like: id, 2016-06-16 14:26:02, 9513 400, 590, 0, 0.
And on this table Im running this SQL query:
SELECT date(test_results.date_time) AS date, test_results.altpn as PN, COUNT(*) AS count
FROM test_results
WHERE (test_results.error1 = 1000 OR test_results.error1 = 1001 OR test_results.error2 = 1000 OR test_results.error2 = 1001 OR test_results.error3 = 1000 OR test_results.error3 = 1001)
GROUP BY date(test_results.date_time), test_results.altpn
ORDER BY date(test_results.date_time), test_results.altpn
This works OK, but I also need to know dates, when COUNT(*) is zero. I saw few topics about LEFT JOIN, but I am not able to modify codes to work with this table.

When you use WHERE, you filter rows with other error codes out and they cannot be counted. Instead, select all rows as usual and LEFT JOIN to a table where zero-rows do not exist:
SELECT *
FROM test_results t1
LEFT JOIN test_results t2 ON t2.id=t1.id
AND
(t2.error1 = 1000 OR t2.error1 = 1001 OR t2.error2 = 1000
OR t2.error2 = 1001 OR t2.error3 = 1000 OR t2.error3 = 1001)
As you see, the result set includes rows with errors of 1000/1001 and NULL values for other errors. Now, simply group and count them:
SELECT date(t1.date_time) AS date, t1.altpn as PN, COUNT(t2.id) AS count
FROM test_results t1
LEFT JOIN test_results t2 ON t2.id=t1.id
AND
(t2.error1 = 1000 OR t2.error1 = 1001 OR t2.error2 = 1000
OR t2.error2 = 1001 OR t2.error3 = 1000 OR t2.error3 = 1001)
GROUP BY date(t1.date_time), t1.altpn
ORDER BY date(t1.date_time), t1.altpn
http://sqlfiddle.com/#!9/9c6236/4

Related

sql only return rows that have certain column values

With my tables above how can I return the user_id(s) which belong to companies that are only in type_id = 34 and 35 and not belonging in type_id= 8. So since comp_id = 3 isnt in type_id= 8 and is in type_id= 34 and 35 therefore results should be user_id=104 and 105
Im looking for users who are in companies both type_id=34 and 35 not either or. If it is not in 34 but in 35 then users from that company should not be returned.
You can use conditional aggregation to get the comp_ids that meet your conditions and with the operator IN get the user_ids:
select user_id
from Table_2
where comp_id in (
select comp_id
from Table_1
group by comp_id
having sum(type_id not in (34, 35)) = 0
and sum(type_id in (34, 35)) = 2
)
If there are other type_ids than 34, 35 and 8 and they are also allowed as long as 34 and 35 exist but not 8 then:
select user_id
from Table_2
where comp_id in (
select comp_id
from Table_1
group by comp_id
having sum(type_id = 8) = 0
and sum(type_id in (34, 35)) = 2
)
This is a simple and readable way to do it.
SELECT DISTINCT user_id
FROM Table_2
WHERE comp_id IN (
SELECT DISTINCT comp_id
FROM Table_1
WHERE type_id IN (34, 35)
MINUS
SELECT DISTINCT comp_id
FROM Table_1
WHERE type_id IN (8)
)
If your database doesn't support SELECT DISTINCT then just use SELECT and add a GROUP BY comp_id to the bottom of each SELECT statement. Capitalisation of keywords is optional. If you already have a big WHERE statement for Table_2 you can use an inner join to the sub-select instead of an IN.
-- EDIT: to avoid the use of MINUS for MySQL --
Confession: I'm not able to test this SQL in MySQL at the moment
SELECT DISTINCT Table_2.user_id
FROM Table_2
INNER JOIN
(SELECT DISTINCT comp_id
FROM Table_1
WHERE type_id IN (34, 35)
) type_include ON Table_2.type_id = type_include.type_id
LEFT JOIN
(SELECT DISTINCT comp_id
FROM Table_1
WHERE type_id IN (8)
) type_exclude ON Table_2.type_id = type_exclude.type_id
AND type_exclude.type_id IS NULL
You can do such a thing with a subquery, in which you specify the condition that should not be met
SELECT DISTINCT t2.user_id
FROM Table_2 t2
JOIN Table_1 t1
ON ((t1.comp_id = t2.comp_id)
AND ((type_id = 34) OR (type_id = 35))
AND t1.comp_id NOT IN (SELECT comp_id FROM Table_1 WHERE type_id = 8)
)
;
PS: For the data in your example, you would not even have to check for the ID being equal to 35 or 34 as there are no other values in your data anyway.
SELECT DISTINCT t2.user_id
FROM Table_2 t2
WHERE t2.comp_id NOT IN (SELECT comp_id FROM Table_1 WHERE type_id = 8)
;
EDIT: As was correctly pointed out, the first query will
also include user_ids for which Table_1 only has rows with either type_id = 34 or type_id = 35 and not only with both
also include user_ids for which Table_1 has rows that additionally to 34 or 35 may have another type_id other than 8 (which is not present in the example, but may happen in other data)
This is by design, as I understood the question requiring this. If this is not the intended result, please look at the answer from #forpas (https://stackoverflow.com/a/63382574/14015737), which yields a result that does neither of the above.

Find the same list ID of four tuples in a stored procedure

I'm writing a procedure...
I have 4 parameters in this procedure.
I want to find the same list ID from a Table using this four parameters.
With other words, if all IDs have same List_ID then return List_ID, if
not return NULL, and admitting that some IDs might be NULL, and some
IDs repeats in that table, so it should not fail if ID is null or
repeats
The difficult part is that not all the times I have that four IDs set , and I might have only two IDs set and other two set as NULL.
For example:
Table A
------------------------
ID List_ID
------------------------
1 10
2 10
3 10
4 10
The only solution I see is something like this:
SET id1 = (Select List_ID From Table_A Where ID = _ID_Param1);
SET id2 = (Select List_ID From Table_A Where ID = _ID_Param2);
SET id3 = (Select List_ID From Table_A Where ID = _ID_Param3);
SET id4 = (Select List_ID From Table_A Where ID = _ID_Param4);
#Then I have to check if all ids are same
IF id1 = id2 = id3 = id4 THEN I found the same List_ID
And sometime _ID_Param is set as NULL, so I might have only 1 or 2 or all 4
Sorry If I'm not explaining this very well... but I don't know how to tell this situation, and my knowledges are limited, I need some help
UPDATE
this is close to what I need:
SELECT
IF( (
MIN( List_ID ) = MAX( List_ID )
AND COUNT( * ) = (Select Count(*) From (SELECT _ID_Param1 AS val
UNION ALL
SELECT _ID_Param2
UNION ALL
SELECT _ID_Param3
UNION ALL
SELECT _ID_Param4) Temp Where Temp.val is not null
) ) , List_ID, NULL
) AS LID
FROM table_a
WHERE ID IN ( _ID_Param1, _ID_Param2, _ID_Param3, _ID_Param4 )
The Only Wrong thing is that it will return the List_ID even if a parameter not exists in table and there is a duplicate of other parameters And it count it twice. How to exclude such case, to check if all params exists
You can do this in one query:
select #all_same := (min(List_ID) = max(List_ID) and count(*) = 4)
from table_a
where id in (_ID_Param1, _ID_Param2, _ID_Param3, _ID_Param4);
This assumes that each id is unique in table_a. That is how your queries are set up.
You can use the COUNT aggregate function to find tuples
Select List_ID, COUNT(*) as tuple
FROM Table_A
WHERE ID = _ID_Param_1 OR ID = _ID_Param_2 ... etc
GROUP BY List_ID
HAVING COUNT(*) = 4;
This will return List_IDs that appears 4 times for a given set of parameters.

update row if count(*) > n

my DB has this structure:
ID | text | time | valid
This is my current code. I'm trying to find a way to do this as one query.
rows = select * from table where ID=x order by time desc;
n=0;
foreach rows{
if(n > 3){
update table set valid = -1 where rows[n];
}
n++
}
I'm checking how many rows exist for a given ID. Then I need to set valid=-1 for all rows where n >3;
Is there a way to do this with one query?
You can use a subquery in the WHERE clause, like this:
UPDATE table
SET valid=-1
WHERE (
SELECT COUNT(*)
FROM table tt
WHERE tt.time > table.time
AND tt.ID = table.ID
) > 3
The subquery counts the rows with the same ID and a later time. This count will be three or less for the three latest rows; the remaining ones would have a greater count, so their valid field would be updated.
Assuming that (id,time) has a UNIQUE constraint, i.e. no two rows have the same id and same time:
UPDATE
tableX AS tu
JOIN
( SELECT time
FROM tableX
WHERE id = #X -- the given ID
ORDER BY time DESC
LIMIT 1 OFFSET 2
) AS t3
ON tu.id = #X -- given ID again
AND tu.time < t3.time
SET
tu.valid = -1 ;
update table
set valid = -1
where id in (select id
from table
where id = GIVEN_ID
group by id
having count(1) >3)
Update: I really like dasblinkenlight's solution because is very neat, but I wanted to try also to do it in my way, a quite verbose one:
update Table1
set valid = -1
where (id, time) in (select id,
time
from (select id,time
from table1
where id in (select id
from table1
group by id
having count(1) >3)
-- and id = GIVEN_ID
order by time
limit 3, 10000000)
t);
Also in SQLFiddle
to do it for all ids, or only for one if you set a where in the a subquery
UPDATE TABLE
LEFT JOIN (
SELECT *
FROM (
SELECT #rn:=if(#prv=id, #rn+1, 1) AS rId,
#prv:=id AS id,
TABLE.*
FROM TABLE
JOIN ( SELECT #prv:=0, #rn:=0 ) tmp
ORDER BY id, TIMESTAMP
) a
WHERE rid > 3
) ordered ON ordered.id = TABLE.id
AND ordered.TIMESTAMP = TABLE.TIMESTAMP
AND ordered.text = TIMESTAMP.text
SET VALID = -1
WHERE rid IS NOT NULL

Select a row with least value of a column using where and group by

Sample table:
id------user_id------grade_id------time_stamp
1---------100----------1001---------2013-08-29 15:07:38
2---------101----------1002---------2013-08-29 16:07:38
3---------100----------1001---------2013-08-29 17:07:38
4---------102----------1003---------2013-08-29 18:07:38
5---------103----------1004---------2013-08-29 19:07:38
6---------105----------1002---------2013-08-29 20:07:38
6---------100----------1002---------2013-08-29 21:07:38
I want to select rows whose user_id = 100 group by grade_id only if its time_stamp is least for that particular grade_id.
so, from the above table, it should be:
row 1 because its time_stamp is least for that value of grade_id(1001)
but not row 2 because I only want 1 row for a particular grade_id
also not row 6 because that particular grade_id has least value for user_id 105.
I tried few things, which are too basic and obviously not worth posting.
Thank You
You could try nested queries:
SELECT grade_id, COUNT(grade_id) FROM SAMPLE_TABLE ST WHERE time_stamp = (SELECT MIN(time_stamp) FROM SAMPLE_TABLE STT WHERE STT.grade_id = ST.grade_id) AND user_id = 100 GROUP BY grade_id;
In this case, the nested query will give you the minimun timestamp for each specific 'grade_id' and you can use it in your WHERE filter.
SELECT t.*
FROM tableX AS t
JOIN
( SELECT grade_id, MIN(time_stamp) AS time_stamp
FROM tableX
GROUP BY grade_id
) AS g
ON g.grade_id = t.grade_id
AND g.time_stamp = t.time_stamp
WHERE t.user_id = 100 ;

SQL Group By And Display With Different Values

I want to create group query where Table values are like this below:
EMP_ID ProjectID
815 1
985 1
815 3
985 4
815 4
And i want output like this
EMP_ID ProjectID1 ProjectID2 ProjectID3
815 1 3 4
985 1 4 0
can anyone know how can i achieve this thing in SQL query.
Thank in advance.
The short way:
Using http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
SELECT
tbl.emp_id,
GROUP_CONCAT( DISTINCT project_id ) project_id_list
FROM tbl
GROUP BY tbl.emp_id
In this case, you have to split/process the concatenated project_id_list string (or NULL) in your application
The long way:
We will use a little trick:
http://dev.mysql.com/doc/refman/5.1/en/example-auto-increment.html
For MyISAM tables you can specify AUTO_INCREMENT on a secondary column
in a multiple-column index. In this case, the generated value for the
AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1
WHERE prefix=given-prefix. This is useful when you want to put data
into ordered groups.
CREATE TEMPORARY TABLE temp (
emp_id INT NOT NULL,
-- project_num will count from 1 to N PER emp_id!
project_num INT NOT NULL AUTO_INCREMENT,
project_id INT NOT NULL,
PRIMARY KEY ( emp_id, project_num )
) ENGINE=MyISAM; -- works only with myisam!
Generate the per-group auto increments:
INSERT INTO temp ( emp_id, project_id )
SELECT emp_id, project_id FROM tbl
Calculate how many project_id columns are needed:
$MAX_PROJECTS_PER_EMP =
SELECT MAX( max_projects_per_emp ) FROM
( SELECT COUNT(*) AS max_projects_per_emp project_id FROM tbl GROUP BY emp_id )
Programmatically create the select expression:
SELECT
temp.emp_id,
t1.project_id AS project_id_1,
t2.project_id AS project_id_2,
t98.project_id AS project_id_98,
t99.project_id AS project_id_99,
FROM temp
LEFT JOIN temp AS t1 ON temp.emp_id = t1.id AND t1.project_num = 1
LEFT JOIN temp AS t2 ON temp.emp_id = t2.id AND t1.project_num = 2
// create $MAX_PROJECTS_PER_EMP lines of LEFT JOINs
LEFT JOIN temp AS t98 ON temp.emp_id = t98.id AND t98.project_num = 98
LEFT JOIN temp AS t99 ON temp.emp_id = t99.id AND t99.project_num = 99