I have data in below structure.
A
A1 A2
B B1
C C1 C2 C3
These information transferred into two table named group1 and group2.
group1 has first level of data and middle level data.
group2 has last level of data and middle level data.
ie
group1
group_name group_id
A 1
A1 2
B 3
C 4
C1 5
C2 6
group2
group2_name parent_id
A1 1
A2 2
B 1
B1 3
C 1
C1 4
C2 5
C3 6
Now i want to get the last level of information under the group A.
My output could be
group2_name
A2
B1
C3
I can fetch the information level 2 by using below query.
select group2.group_name from group2
inner join
group1 on group1.group_id = group2.parent_id
where group1.group_name = 'A'
How can get the above output?
Here is SQLFIDDLE Demo
Kindly help me.
You could use this:
select
group2.group_name
from
group2 left join group1
using(group_name)
where
group1.group_name is null
and group2.group_name like 'A%'
that returns all elements from table group2 that are not present in table group1.
Or (depending on how your database is structured) also this:
select
concat(left(group_name,1),
case when max(mid(group_name,2,length(group_name)-1)+0)>0 then
max(mid(group_name,2,length(group_name)-1)+0)
else '' end)
from group2
where group2.group_name like 'A%'
group by left(group_name,1)
here I am grouping for the first character of the string, and getting the maximum value of the numeric value.
Related
Given this DB table:
A_column B_column
---------------
A1 1
A1 2
A1 2
A2 1
A2 1
A2 1
A3 2
A3 3
A3 4
A3 5
How do I write SQL SELECT query to print out number of unique values in B_column per value in A_column, so output would be like this:
A1 2
A2 1
A3 4
I tried this, but doesn't seem to work properly:
SELECT A_column, count(B_column) FROM table GROUP BY A_column
Use distinct:
SELECT A_column, count(distinct B_column) FROM table GROUP BY A_column
Suppose I have a table that records changes to my database over time:
TimeOfChange FieldA FieldB FieldC
-------------------------------------
2019-01-01 A1 B1 C1 /*(R1)*/
2019-01-02 A2 B2 C1 /*(R2)*/
2019-01-03 A2 B2 C1 /*(R3)*/
2019-01-05 A1 B1 C2 /*(R4)*/
2019-01-07 A1 B1 C1 /*(R5)*/
My database has many rows where nothing significant changed, eg row (R3) is the same as (R2).
I would like to remove these rows. I have found many references on how to use a common table expression to remove duplicate rows from the table. So it's possible to remove the duplicate (ignoring the TimeOfChange column) rows. But this will remove (R5) as well because it is the same as R1. I only want to remove the rows that have the same ABC values as the previous row, when ordered by the TimeOfChange column. How do I do that?
edit: You can assume that TimeOfChange values are all unique
Assuming the TimeOfChange is unique, you can do:
delete
from data
where TimeOfChange in (
select TimeOfChange
from (
select d2.TimeOfChange
from data d1
join data d2
where d2.TimeOfChange in (
select min(x.TimeOfChange)
from data x
where x.TimeOfChange>d1.TimeOfChange
) and d1.FieldA=d2.FieldA and d1.FieldB=d2.FieldB and d1.FieldC=d2.FieldC
) as q
);
So you first want to determine which rows are the "next" and then check if the "next" has the same values as the "current". For those the "next" would form a result set that you want to use in DELETE. The select * from data is there to circumvent the reuse of the table in DELETE and in the subquery.
You probably will get much better performance if you separate the logic into a stored procedure and store the id's for rows to be deleted into a temp table.
See DB Fiddle
Presuming, you really meant "when the same A, B, C occurred on the most recent day prior that had any data", this should be usable to identify the rows that need removed:
SELECT t2.TimeOfChange, t2.FieldA, t2.FieldB, t2.FieldC
FROM (
SELECT tMain.TimeOfChange, tMain.FieldA, tMain.FieldB, tMain.FieldC
, MAX(tPrev.TimeOfChange) AS prevTimeOfChange
FROM t AS tMain
LEFT JOIN t AS tPrev ON t.TimeOfChange> tPrev.TimeOfChange
GROUP BY tMain.TimeOfChange, tMain.FieldA, tMain.FieldB, tMain.FieldC
) AS t2
INNER JOIN t AS tPrev2
ON t2.prevTimeOfChange = tPrev2.TimeOfChange
AND t2.FieldA = tPrev2.FieldA
AND t2.FieldB = tPrev2.FieldB
AND t2.FieldC = tPrev2.FieldC
This can then be used in a DELETE with some indirection to force a temp table to be created.
DELETE td
FROM t AS td
WHERE (td.TimeOfChange, td.FieldA, td.FieldB, td.FieldC)
IN (SELECT * FROM ([the query above]) AS tt) -- Yes, you have to wrap the query from above in a select * so mysql will not reject it.
;
However, after getting this far, what happens when....
2019-01-01 A1 B1 C1
2019-01-02 A2 B2 C1
2019-01-03 A2 B2 C1
2019-01-04 A1 B1 C2
2019-01-05 A1 B1 C3
2019-01-05 A1 B1 C1
2019-01-06 A1 B1 C3
2019-01-07 A1 B1 C1
becomes
2019-01-01 A1 B1 C1
2019-01-02 A2 B2 C1
2019-01-04 A1 B1 C2
2019-01-05 A1 B1 C3
2019-01-05 A1 B1 C1
2019-01-07 A1 B1 C1
Does a second pass now need made to remove the 2019-01-07 entry?
Are you going to run the query repeatedly until no rows are affected?
I have a table with the below values
name symbol current value
a a1 1
a a2 2
a a3 4
a a4 3
a a5 5
b b1 6
b b2 7
b b3 8
c c1 1
c c2 2
c c3 3
c c4 3
c c5 5
d d1 6
d d2 6
Required : To find the average of the current value grouping by name , yet show all results . ie ; the result show be like below ;
name symbol current value Required
a a1 1 =current value/(sum of all 'a' current values)
a a2 2 =current value/(sum of all 'a' current values)
a a3 4 =current value/(sum of all 'a' current values)
a a4 3 =current value/(sum of all 'a' current values)
a a5 5 =current value/(sum of all 'a' current values)
b b1 6 = current value /(sum of all 'b' current values)
b b2 7 = current value /(sum of all 'b' current values)
b b3 8 = current value /(sum of all 'b' current values)
Similarly for all names
Join to a subquery which finds the averages:
SELECT t1.*,
CASE WHEN t2.avg > 0 THEN t1.current_value / t2.avg ELSE 0 END AS avg
FROM yourTable t1
INNER JOIN
(
SELECT name, SUM(current_value) AS avg
FROM yourTable
GROUP BY name
) t2
ON t1.name = t2.name;
The CASE expression is necessary to protect against a possible divide by zero, which could happen if a given name happen to have all zero current values. In that case, I default the average to zero.
try this logic :)
SELECT
*
FROM
table_listing_sample
WHERE
NAME = 'a'
AND NAME = 'b'
ORDER BY
id
I want to copy values of specific table field from one list of ids (origin), to another list (destination). (note that for each couple there is a different value to copy).
Original table:
id fieldA
1 A
2 B
3 C
Destination table:
id fieldA
11 a
22 b
33 c
Map list:
This is list of ids I need to provide in my query
originIds destinationIds
1 11
2 22
3 33
State of destination table after update:
This is the wanted results
id fieldA
11 A
22 B
33 C
I would appreciate assistance with building such update query.
Either have a table to contain your mappings. Then:
update destination_table d
join mapping_table m on m.destination_id = d.id
join original_table o on o.id = m.origin_id
set d.fielda = a.fielda;
Or build it on-the-fly:
update destination_table d
join
(
select 1 as origin_id, 11 as destination_id
union all
select 2 as origin_id, 22 as destination_id
union all
select 3 as origin_id, 33 as destination_id
) m on m.destination_id = d.id
join original_table o on o.id = m.origin_id
set d.fielda = a.fielda;
I have a mysql query which returns more than one row for few ID's with different values. In such a case i need to eliminate the ID's with certain data.
Example
I need to elimate A1 for all those ID's which has more than one name and else if it has only one value A1 then i should be able to display it.
Result Should look like:
ID Name Value
1 A1 AA
1 B1 AB
2 C1 CC
3 A1 AA
4 A1 AA
4 E1 AD
4 B1 AB
Please a solution for this
This is probably very ugly, but at least gets the job done:
select id, name, value from (
select f.*, c.count
from <TABLE> f, (select *, count(*) as 'count' from <TABLE> group by id) c
where f.id = c.id
) c
where (name = 'A1' and count = 1) or name != 'A1'
group by id
you will get the 'first' row for remaining (not random) for id = 4