Ignore null entries in sql - mysql

The example below builds a table that extracts the first two score values by userId and passageId. How can I select only records where each record in the new table contains at least two scores (i.e. ignore records where score2 is null)?
Example
Code:
drop table if exists simon;
drop table if exists simon2;
Create table simon (userId int, passageId int, score int);
Create table simon2 (userId int, passageId int, score1 int,score2 int);
INSERT INTO simon (userId , passageId , score )
VALUES
(10, 1, 2),
(10, 1, 3),
(10, 2, 1),
(10, 2, 1),
(10, 2, 5),
(11, 1, 1),
(11, 2, 2),
(11, 2, 3),
(11, 3, 4);
insert into simon2(userId,passageId,score1,score2)
select t.userId, t.passageId,
substring_index(t.scores,',',1) as score1,
(case when length(t.scores) > 1 then substring_index(t.scores,',',-1)
else null
end
) as score2
from
(select userId,passageId,
substring_index (group_concat(score separator ','),',',2) as scores
from simon
group by userId,passageId) t;
select *from simon2;
This is what I get now:
userId passageId score1 score2
1 10 1 2 3
2 10 2 1 1
3 11 1 1 NULL
4 11 2 2 3
5 11 3 4 NULL
This is what I want:
userId passageId score1 score2
1 10 1 2 3
2 10 2 1 1
4 11 2 2 3

Just add this around your query
Select * from ( ...... ) x where score2 is not null

You have no ordering specifying what score goes into score_1 and score_2, so I'll just use min() and max(). You can then do the logic as:
select s.userid, s.passageid,
max(score) as score_1, max(score) as score_2
from simon s
group by s.userid, s.passageid
having count(*) >= 2;
This doesn't give you exactly the same results for 10/2. However, your results are arbitrary because the group_concat() has no order by. SQL tables represent unordered sets. There is no ordering unless you specify it.
If you want an ordering, then define the table as:
Create table simon (
simonId int auto_increment primary key,
userId int,
passageId int,
score int
);
And then use the simonId column appropriately.

Related

Count ocurrences and show the last ocurrence in MYSQL

I have this table
serie number
----- ------
A 1
A 2
A 3
A 5
B 1
B 3
I want to show a result like this
serie ocurrences last_number_value
----- ---------- -----------------
A 4 5
B 2 3
I managed to get the ocurrences number with
SELECT serie, number, COUNT(*) AS ocurrences FROM table_name GROUP BY serie
But, how to show the last number too?
This will get you the result your looking for:
CREATE TABLE test (
serie varchar(1) NOT NULL,
num int(1) NOT NULL
);
INSERT INTO test (serie, num) VALUES
('A', 1),
('A', 2),
('A', 3),
('A', 5),
('B', 1),
('B', 3);
If the row order matters:
SELECT
a.serie,
(SELECT count(*) FROM test WHERE serie=a.serie) as occurances,
a.num
FROM
test AS a
LEFT JOIN test AS b on a.serie=b.serie AND a.num<b.num
WHERE
b.serie IS NULL;
If the order or the rows do NOT matter:
SELECT
a.serie,
COUNT(*) AS occurances,
MAX(a.num) AS last_number_value
FROM
test AS a
GROUP BY a.serie;

SQL - Return the order of products purchased by adding new column

I'm working on another SQL query.
I have the following table.
PURCHASES
ID CUST_ID PROD_CODE PURCH_DATE
1 1 'WER' 01/12/2012
2 2 'RRE' 02/10/2005
3 3 'RRY' 02/11/2011
4 3 'TTB' 15/05/2007
5 3 'GGD' 20/06/2016
6 2 'SSD' 02/10/2011
I'm trying to add another column PURCH_COUNT that would display the purchase count for the CUST_ID based on PURCH_DATE.
If this is a first purchase it would return 1, if second then 2, and so on.
So the result I'm hoping is:
ID CUST_ID PROD_CODE PURCH_DATE PURCH_COUNT
1 1 'WER' 01/12/2012 1
2 2 'RRE' 02/10/2005 1
3 3 'RRY' 02/11/2011 2
4 3 'TTB' 15/05/2007 1
5 3 'GGD' 20/06/2016 3
6 2 'SSD' 02/10/2011 2
Thanks in advance!
Sample Data
DECLARE #Table1 TABLE
(ID int, CUST_ID int, PROD_CODE varchar(7), PURCH_DATE datetime)
;
INSERT INTO #Table1
(ID, CUST_ID, PROD_CODE, PURCH_DATE)
VALUES
(1, 1, 'WER', '2012-01-12 05:30:00'),
(2, 2, 'RRE', '2005-02-10 05:30:00'),
(3, 3, 'RRY', '2011-02-11 05:30:00'),
(4, 3, 'TTB', '2008-03-05 05:30:00'),
(5, 3, 'GGD', '2017-08-06 05:30:00'),
(6, 2, 'SSD', '2011-02-10 05:30:00')
;
IN SQL :
select ID,
CUST_ID,
PROD_CODE,
PURCH_DATE,
ROW_NUMBER()OVER(PARTITION BY CUST_ID ORDER BY (SELECT NULL))RN
from #Table1
In MySql :
SELECT a.ID, a.CUST_ID,a.PROD_CODE,a.PURCH_DATE, (
SELECT count(*) from #Table1 b where a.CUST_ID >= b.CUST_ID AND a.ID = b.ID
) AS row_number FROM #Table1 a
Use a correlated sub-query to get the counts per customer.
SELECT t.*,
(SELECT 1+count(*)
FROM table1
WHERE t.cust_id = cust_id
AND t.purch_date > purch_date) as purch_cnt
FROM table1 t
ORDER BY cust_id,purch_date
SQL Fiddle
Any correlated subquery or window function can be expressed as a join, too. Sometimes a join is easier to understand, or is produced from components you can re-use, and sometimes the DBMS doesn't support the fancier feature. (AFAIK, a subquery in a SELECT clause is nonstandard.)
create table T
(ID, CUST_ID, PROD_CODE, PURCH_DATE);
INSERT INTO T
(ID, CUST_ID, PROD_CODE, PURCH_DATE)
VALUES
(1, 1, 'WER', '2012-01-12 05:30:00'),
(2, 2, 'RRE', '2005-02-10 05:30:00'),
(3, 3, 'RRY', '2011-02-11 05:30:00'),
(4, 3, 'TTB', '2008-03-05 05:30:00'),
(5, 3, 'GGD', '2017-08-06 05:30:00'),
(6, 2, 'SSD', '2011-02-10 05:30:00')
;
select PURCH_COUNT, T.*
from T join (
select count(b.ID) as PURCH_COUNT
, a.CUST_ID, a.PURCH_DATE
from T as a join T as b
on a.CUST_ID = b.CUST_ID
and b.PURCH_DATE <= a.PURCH_DATE
group by a.CUST_ID, a.PURCH_DATE
) as Q
on T.CUST_ID = Q.CUST_ID
and T.PURCH_DATE = Q.PURCH_DATE
;
Output of subquery:
PURCH_COUNT CUST_ID PURCH_DATE
----------- ---------- -------------------
1 1 2012-01-12 05:30:00
1 2 2005-02-10 05:30:00
2 2 2011-02-10 05:30:00
1 3 2008-03-05 05:30:00
2 3 2011-02-11 05:30:00
3 3 2017-08-06 05:30:00
Output of query:
PURCH_COUNT ID CUST_ID PROD_CODE PURCH_DATE
----------- ---------- ---------- ---------- -------------------
1 1 1 WER 2012-01-12 05:30:00
1 2 2 RRE 2005-02-10 05:30:00
2 3 3 RRY 2011-02-11 05:30:00
1 4 3 TTB 2008-03-05 05:30:00
3 5 3 GGD 2017-08-06 05:30:00
2 6 2 SSD 2011-02-10 05:30:00

Insert records to a table with loop from an other table data

I have two tables like this:
SupplyList (IDSupply is the primary key)
IDSupply PartName Qty
--------- --------- ----
1 C 10
2 B 4
SupplyIndex (IDSupply and Index are the compound primary key)
IDSupply PartName Index
--------- --------- ------
1 C 2
1 C 3
1 C 7
1 C 9
1 C 10
These tables are related to each other with IDSupply.
I want to insert missed records to SupplyIndex table by a query in SQL. In other words, my expected result is SupplyIndex table like below (Index must include numbers from 1 to Qty from SupplyList table)
IDSupply PartName Index
--------- --------- ------ (result)
1 C 1
1 C 2
1 C 3
1 C 4
1 C 5
1 C 6
1 C 7
1 C 8
1 C 9
1 C 10
2 B 1
2 B 2
2 B 3
2 B 4
I did this job in my VB.Net application before and now I want to do it in SQL Server directly.
Would you please help me?
Thanks
Test Data:
create table #supplylist
(
idsupply int,
partname char(20),
qty int
)
insert into #supplylist
select 1,'a',10
union all
select 2,'c',4
create table #idsupply
(
idsupply int,
partname char(20),
indexx int
)
insert into #idsupply
select 1,'a',10
union all
select 2,'c',3
I used Numbers table to accomplish this
with cte
as
(
select
idsupply,
partname,n
from
#supplylist t
cross apply
(
select n from numbers where n <=qty
)b
--final part to check and isnert in other table..same query as above with insert and exists
with cte
as
(
select
idsupply,
partname,n
from
#supplylist t
cross apply
(
select n from numbers where n <=qty
)b
)
insert into #idsupply
select * from cte t1 where not exists (select 1 from #idsupply t2 where t2.indexx=t1.n)
Create Table #SupplyList(IDSupply int Primary Key, PartName char(10),Qty int);
Insert #SupplyList(IDSupply, PartName, Qty) Values
(1, 'C', 10),
(2, 'B', 4);
Create Table #SupplyIndex(IDSupply int, PartName char(10), [Index] int Primary Key (IdSupply, [Index]));
Insert #SupplyIndex(IDSupply, PartName, [Index]) Values
(1, 'C', 2),
(1, 'C', 3),
(1, 'C', 7),
(1, 'C', 9),
(1, 'C', 10);
;With cteMax As
(Select Max([Index]) As MaxIndex From #SupplyIndex),
cteNumbers As
(Select 1 As Number
Union All
Select Number + 1
From cteNumbers n
Cross Join cteMax m
Where n.Number < m.MaxIndex)
Merge #SupplyIndex t
Using (Select sl.IDSupply, sl.PartName, n.Number As Qty From #SupplyList sl Inner Join cteNumbers n On n.Number <= sl.Qty) s
On s.IDSupply = t.IDSupply And s.PartName = t.PartName And s.Qty = t.[Index]
When Not Matched Then Insert(IDSupply, PartName, [Index]) Values(s.IDSupply, s.PartName, s.Qty);
Select * From #SupplyIndex;
go
Drop Table #SupplyList;
go
Drop Table #SupplyIndex

finding number of repeated users who have interacted with a category

I am new to sql and am trying to figure out the following..
Imagine the following table:
user_id, category_id
1, 12344
1, 12344
1, 12345
2, 12345
2, 12345
3, 12344
3, 12344
and so on..
I want to find number of repeated users each category got..
so, in example above..
12344, 2 (because user_id 1 and 3 are repeated users)
12345, 1 (user_id 2 is repeated user.. 1 is not as that user visited just once)
How do i figure this out in sql/hive?
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,user_id INT NOT NULL
,category_id INT NOT NULL
);
INSERT INTO my_table (user_id,category_id) VALUES
(1, 12344),
(1, 12344),
(1, 12345),
(2, 12345),
(2, 12345),
(3, 12344),
(3, 12344);
SELECT category_id
, COUNT(*) total
FROM
( SELECT x.*
FROM my_table x
JOIN my_table y
ON y.user_id = x.user_id
AND y.category_id = x.category_id
AND y.id < x.id
) a
GROUP
BY category_id;
+-------------+-------+
| category_id | total |
+-------------+-------+
| 12344 | 2 |
| 12345 | 1 |
+-------------+-------+
It's a little hard to follow what you're looking for, but test this:
select category_id, count(user_id) from (Select category_id, user_id, count(table_primary_id) as 'total' from tablename group by category_id, user_id) a where total > 1 group by category_id
The subquery counts the number of times a user visited a category, and the outside query should count the number of users who visited a category more than once.

Running aggregate column among pairs of varying order

If I have such data:
Id, Team1, Team2, Score1, Score2
--------------------------------
1, Aaa, Bbb, 10, 8
2, Aaa, Bbb, 6, 8
3, Aaa, Bbb, 6, 5
4, Bbb, Aaa, 9, 7
5, Aaa, Ccc, 12, 6
How I can get a column with difference between scores among teams (running aggregate)?
Example:
Id, Team1, Team2, Score1, Score2 Abs(DIFF)
-------------------------------- ---------
1, Aaa, Bbb, 10, 8 2
2, Aaa, Bbb, 6, 8 0
3, Aaa, Bbb, 6, 5 1
4, Bbb, Aaa, 7, 9 3
5, Aaa, Ccc, 12, 6 6
Please notice two things in the above example:
Order of teams among pair may vary (like in between row 1 and 4)
Table may contain many team pairs
Is it possible to get this DIFF column without using cursor or an update trick?
Query has to run on SQL Server 2008.
I rarely do DB work so simple explanations are welcome.
UPDATE Additional explanation:
In the first row the DIFF is 2 because team Aaa won by two points
In the second row the DIFF is 0 because this time team Bbb won by two points
In the third row the DIFF is 1 because team Aaa won again (by one point)
In the forth row the DIFF is 3 because team Aaa won again (by two points)
So by looking at the DIFF value, one should be able to answer a question like "what is the current difference in score between teams?".
Going with your second sample data (row 3 is tied in first sample, and row 4's score switch sides):
declare #t table (Id int not null,Team1 char(3) not null,Team2 char(3) not null,
Score1 int not null,Score2 int not null)
insert into #t(Id, Team1, Team2, Score1, Score2) values
(1, 'Aaa', 'Bbb', 10, 8),
(2, 'Aaa', 'Bbb', 6, 8),
(3, 'Aaa', 'Bbb', 6, 5),
(4, 'Bbb', 'Aaa', 7, 9),
(5, 'Aaa', 'Ccc', 12, 6)
;With NormTeams as (
select Id,
CASE WHEN Team1 < Team2 THEN Team1 ELSE Team2 END as Team1,
CASE WHEN Team1 < Team2 THEN Team2 ELSE Team1 END as Team2,
CASE WHEN Team1 < Team2 THEN Score1 ELSE Score2 END as Score1,
CASE WHEN Team1 < Team2 THEN Score2 ELSE Score1 END as Score2,
(Score1 - Score2) * CASE WHEN Team1 < Team2 THEN 1 ELSE -1 END as ScoreDiff
from #t
), MatchingTeams as (
select *,ROW_NUMBER() OVER (PARTITION By Team1,Team2 ORDER BY ID) as rn
from NormTeams
)
select *,
(SELECT SUM(ScoreDiff) from MatchingTeams mt2 where
mt2.Team1 = mt1.Team1 and mt2.Team2 = mt1.Team2 and
mt2.rn <= mt1.rn) as RunningDiff
from MatchingTeams mt1
There's unfortunately nothing much that can make it neater until you can use the 2012 windowed aggregates.
Result:
Id Team1 Team2 Score1 Score2 ScoreDiff rn RunningDiff
----------- ----- ----- ----------- ----------- ----------- -------------------- -----------
1 Aaa Bbb 10 8 2 1 2
2 Aaa Bbb 6 8 -2 2 0
3 Aaa Bbb 6 5 1 3 1
4 Aaa Bbb 9 7 2 4 3
5 Aaa Ccc 12 6 6 1 6
You'll notice that the first thing I do is the obvious - switch teams and scores around, if necessary, so that the rest of the query can assume that they do appear in a consistent order.
try this:
;WITH CTE as
(
select id,Team1,Team2,Score1,Score2,0 as RS1,0 as RS2,ABS(Score1-Score2) as DIFF from Table1 where id=1
UNION ALL
select t1.id,t1.Team1,t1.Team2,t1.Score1,t1.Score2,c.score1+t1.Score1,c.score2+t1.Score2,ABS((c.score1+t1.Score1)-(c.score2+t1.Score2)) as DIFF
from Table1 t1 inner join CTE c
on c.id+1=t1.id and ((t1.Team1 = c.team1 and t1.Team2 = c.Team2) or (t1.Team2 = c.team1 and t1.Team1 = c.Team2))
)
,CTE1 As
(
select * from CTE
union
select id,Team1,Team2,Score1,Score2,0,0,ABS(Score1-Score2) as DIFF from Table1 where id not in(select id from CTE)
)
select id,Team1,Team2,Score1,Score2,DIFF from CTE1
You could try this:
Edit: Fixed for team1 & team2 swaps.
; WITH CTE AS (
SELECT
Id
, Team1
, Team2
, Score1
, Score2
, ROW_NUMBER() OVER (PARTITION BY X.Teams ORDER BY ???) AS Row
FROM MyTable
CROSS APPLY (
SELECT
Team1 + '_' + Team2 -- Use something else than '_' if this caracter can exist in team's names
WHERE Team1 < Team2
UNION ALL
SELECT
Team2 + '_' + Team1
WHERE Team1 > Team2
) AS X(Teams)
)
SELECT
T1.Id
, T1.Team1
, T1.Team2
, T1.Score1
, T1.Score2
, ABS((T1.Score1 + ISNULL(T2.Score1, 0)) - (T1.Score2 + ISNULL(T2.Score2, 0))) AS Abs(DIFF)
FROM CTE AS T1
OUTER APPLY (
SELECT
SUM(T2.Score1) AS Score1
, SUM(T2.Score2) AS Score2
FROM CTE AS T2
WHERE T2.Teams = T1.Teams
AND T2.Row < T1.Row
) AS T2