Selecting column from one table and count from another - sql

t1
id | name | include
-------------------
1 | foo | true
2 | bar | true
3 | bum | false
t2
id | some | table_1_id
-------------------------
1 | 42 | 1
2 | 43 | 1
3 | 42 | 2
4 | 44 | 1
5 | 44 | 3
Desired output:
name | count(some)
------------------
foo | 3
bar | 1
What I have currently from looking through other solutions here:
SELECT a.name,
COUNT(r.some)
FROM t1 a
JOIN t2 r on a.id=r.table_1_id
WHERE a.include = 'true'
GROUP BY a.id,
r.some;
but that seems to get me
name | count(r.some)
--------------------
foo | 1
foo | 1
bar | 1
foo | 1
I'm no sql expert (I can do simple queries) so I'm googling around as well but finding most of the solutions I find give me this result. I'm probably missing something really easy.

Just remove the second column from the group by clause
SELECT a.name,
COUNT(r.some)
FROM t1 a
JOIN t2 r on a.id=r.table_1_id
WHERE a.include = 'true'
GROUP BY a.name
Columns you want to use in an aggregate function like sum() or count() must be left out of the group by clause. Only put the columns in there you want to be unique outputted.

This is because multiple column group requires the all column values to be same.
See this link for more info., Using group by on multiple columns
Actually in you case., if some are equal, table_1_id is not equal (And Vice versa). so grouping cannot occur. So all are displayed individually.
If the entries are like,
id | some | table_1_id
-------------------------
1 | 42 | 1
2 | 43 | 1
3 | 42 | 2
4 | 42 | 1
Then the output would have been.,
name | count
------------------
foo | 2 (for 42)
foo | 1 (for 43)
bar | 1 (for 42)
Actually, if you want to group on 1 column as Juergen said, you could remove r.some; from groupby clause.

Related

Can I generate a map that shows a particular row was in a particular group in SQLite?

Say I have the following data:
+--------+-------+
| Group | Data |
+--------+-------+
| 1 | row 1 |
| 1 | row 2 |
| 1 | row 3 |
| 20 | row 1 |
| 20 | row 3 |
| 10 | row 1 |
| 10 | row A |
| 10 | row 2 |
| 10 | row 3 |
+--------+-------+
Is it possible to draw a map that shows which groups have which rows? Groups may not be contagious, so they can be placed into a separate table and use the row index for the string index instead. Something like this:
+-------+
| Group |
+-------+
| 1 |
| 20 |
| 10 |
+-------+
+-------+----------------+
| Data | Found in group |
+-------+----------------+
| row 1 | 111 |
| row A | 1 |
| row 2 | 1 1 |
| row 3 | 111 |
+-------+----------------+
Where the first character represents Group 1, the 2nd is Group 20 and the 3rd is Group 10.
Ordering of the Group rows isn't critical so long as I can reference which row goes with which character.
I only ask this because I saw this crazy example in the documentation generating a fractal, but I can't quite get my head around it.
Is this doable?
To find the missing values, first thing is to prepare a dataset which have all possible combination. You can achieve that using CROSS JOIN.
Once you have that DataSet, compare it with the actual DataSet.
Considering the Order by is done in the Grp column, you can achieve it using below.
SELECT
a.Data,group_concat(case when base.Grp is null then "." else "1" end,'') as Found_In_Group
,group_concat(b.Grp) as Group_Order
FROM
(SELECT Data FROM yourtable Group By Data)a
CROSS JOIN
(SELECT Grp FROM yourtable Group By Grp Order by Grp)b
LEFT JOIN yourtable base
ON b.Grp=base.Grp
AND a.Data=base.Data
GROUP BY a.Data
Note: Considered . instead of blank for better visibility to represent missing Group.
Data
Found_In_Group
Group_Order
row 1
111
1,10,20
row 2
11.
1,10,20
row 3
111
1,10,20
row A
.1.
1,10,20
Demo: Try here
SELECT Data, group_concat("Group") AS "Found in group"
FROM yourtable
GROUP BY Data
will give you a CSV list of groups.

Update rows in one table depending on a column in another table

I have 2 tables in different databases in SQL Server.
database1.table_A
id | name | present |
-----|------------|-----------|
1 | jon | 1 |
2 | ham | 0 |
3 | sam | 1 |
7 | tom | 1 |
database2.table_B
absentid |
----------|
1 |
5 |
7 |
For every id value present in table_B, I want the value of present in table_A to be 0. So, my final result should look like -
id | name | present |
-----|------------|-----------|
1 | jon | 0 |
2 | ham | 0 |
3 | sam | 1 |
7 | tom | 0 |
I want to confirm if the following query I wrote is correct or if there are any better ways to do this:
update database1.table_A
set present=0
FROM database1.table_A t1
inner join
database2.table_B t2
ON t1.id = t2.absentid;
If you want to set present = 1 if they are not in the table, then you would use left join:
update t1
set present = (case when t2.absentid is null then 1 else 0 end)
from database1.table_A t1 left join
database2.table_B t2
on t1.id = t2.absentid;
Otherwise, if you want to keep the value in that case, your version is fine.

Select two tables side by side

I have two tables filled with two files of which table1 always has one row more than the second.
I want to merge the two tables into one. When I use a right join e.g.
select *
from table2
right join table1 on table1.id = table2.id and table1.eq_nb = table2.eq_nb
I will have the 4 combinations for eq_nb = 25 because it is repeated two times.
But I rather want to stick the columns side by side
To know, I don't have conditions on arrival and depart time of each eq_nb (I can't add something like datediff(second,table1.arrival_time,table2.depart_time) < X )
table1:
id | eq_nb | arival_time
-------------------------------------
1 | 25 | 05:30:15.231
-------------------------------------
2 | 50 | 06:30:15.231
-------------------------------------
3 | 7 | 07:30:15.231
-------------------------------------
1 | 25 | 08:30:15.231
-------------------------------------
5 | 27 | 09:30:15.231
-------------------------------------
table2:
id | eq_nb | depart_time
----------------------------------
1 | 25 | 05:31:15.231
----------------------------------
2 | 50 | 06:31:15.231
----------------------------------
3 | 7 | 07:31:15.231
----------------------------------
1 | 25 | 08:31:15.231
----------------------------------
desired result:
id | eq_nb | arrival_time | depart_time
-------------------------------------------------------
1 | 25 | 05:30:15.231 | 05:31:15.231
-------------------------------------------------------
2 | 50 | 06:30:15.231 | 06:31:15.231
-------------------------------------------------------
3 | 7 | 07:30:15.231 | 07:31:15.231
-------------------------------------------------------
1 | 25 | 08:30:15.231 | 08:31:15.231
--------------------------------------------------------
5 | 27 | 09:30:15.231 | NULL
--------------------------------------------------------
left join should do what you want:
select *
from table1 t1 left join
table2 t2
on t1.id = t2.id and t1.eq_nb = t2.eq_nb;
Given your data, the ids are unique, so there should be no duplication. Note: This is equivalent to your first query; left join is typically easier to follow because all the rows in the first table are in the result set.
Here is a db<>fiddle, illustrating that it works.

Returning rows with the same ID but exclude some on second column

I've seen similar questions about but not quite hitting the nail on the head for what I need. Lets say I have a table.
+-----+-------+
| ID | Value |
+-----+-------+
| 123 | 1 |
| 123 | 2 |
| 123 | 3 |
| 456 | 1 |
| 456 | 2 |
| 456 | 4 |
| 789 | 1 |
| 789 | 2 |
+-----+-------+
I want to return DISTINCT IDs but exclude those that have a certain value. For example lets say I don't want any IDs that have a 3 as a value. My results should look like.
+-----+
| ID |
+-----+
| 456 |
| 789 |
+-----+
I hope this makes sense. If more information is needed please ask and if this has been answered before please point me in the right direction. Thanks.
You can use group by and having:
select id
from t
group by id
having sum(case when value = 3 then 1 else 0 end) = 0;
The having clause counts the number of "3"s for each id. The = 0 returns only returns groups where the count is 0 (i.e. there are no "3"s).
You can use not exists :
select distinct t.id
from table t
where not exists (select 1 from table t1 where t1.id = t.id and t1.value = 3);
Try this:
select id from tablename
group by id
having (case when value=3 then 1 else 0 end)=0
You can also use EXCEPT for comparing following two data sets that will give the desired result set
select distinct Id from ValuesTbl
except
select Id from ValuesTbl where Value = 3

mysql - get details from 2 tables with 1 query

I have 2 mysql tables:
The first table table1:
| ID | NAME |
| 1 | cat |
| 2 | mouse |
| 3 | mouse |
| 4 | mouse |
| 5 | mouse |
| 6 | mouse |
The secound table table2:
| ID | NAME_NA |
| 1 | cat |
| 2 | mouse |
| 3 | cd_rom |
| 4 | dvd_rw |
| 5 | house |
And i want to output this :
mouse - 5 entries
cat - 1 entry
cd_rom - 0 entries
dvd_rw - 0 entries
house - 0 entries
Use a LEFT JOIN:
SELECT x.name_na AS name,
COALESCE(COUNT(y.name), 0) AS num_entries
FROM TABLE2 x
LEFT JOIN TABLE1 y ON y.name = x.name_na
...to get:
name num_entries
--------------------
mouse 5
cat 1
cd_rom 0
dvd_rw 0
house 0
SELECT `table1`.`NAME`, IFNULL(COUNT(*), 0) as entries FROM
`table2` LEFT JOIN `table1` ON `table1`.`NAME` = `table2`.`NAME_NA`
GROUP BY `table1`.`NAME`;
The LEFT JOIN creates rows for each item in the "left" table (table2 here) even if there's no match in table1, and also will create multiple rows for an item in table2 if there are multiple matching rows in table1 for that item.
Then if you GROUP BY the name and use a COUNT() function, you can get the count of how many matches each item in table2 has in table1.
The IFNULL() is utilized because COUNT() will return NULL if there weren't any matches for that item in table1, so we substitute in 0 instead.