SQL Subquery with a single table result with NULL - sql

I have tried to select with sub queries in only a single table. but for some reason i got a NULL result.
The table I have look like this.
| id | ItemCode | ItemAmount | Counter |
-----------------------------------------
| 1 | 001 | 1 | Counter-1 |
| 2 | 001 | 1 | Counter-2 |
| 3 | 002 | 2 | Counter-1 |
| 4 | 002 | 2 | Counter-2 |
| 5 | 002 | 1 | Counter-2 |
I have tried this SQL :
select
id,
itemCode,
(select ItemAmount where Counter = 'Counter-1') as 'Count 1 Result',
(select Counter where Counter = 'COUNTER-1') as 'Count Is 1',
(select ItemAmount where Counter = 'Counter-2') as 'Count 2 Result',
(select Counter where Counter = 'COUNTER-2') as 'Count Is 2',
from
My_Table
and the result I got is :
| id | ItemCode | Count 1 Result | Count Is 1 | Count 2 Result | Count Is 2 |
----------------------------------------------------------------------
| 1 | 001 | 1 | Counter-1 | NULL | NULL |
| 2 | 001 | NULL | NULL | 1 | Counter-2 |
| 3 | 002 | 2 | Counter-1 | NULL | NULL |
| 4 | 002 | NULL | NULL | 2 | Counter-2 |
| 5 | 002 | NULL | NULL | 1 | Counter-2 |
As you can see, i got NULL result with the NULL Value. How can i do it with the result like this :
| id | ItemCode | Count 1 Result | Count Is 1 | Count 2 Result | Count Is 2 |
-------------------------------------------------------------------------
| 1 | 001 | 1 | Counter-1 | 2 | Counter-2 |
--------------------------------------------------------------------------
| 2 | 002 | 2 | Counter-1 | 3 | Counter-2 |
--------------------------------------------------------------------------
I want to make it no NULL value anymore and there is no double Item Code with the SUM Item Amount if the counter and item code is have the same value.
Is that even possible to do it with one table ? if it is how do i do that. thanks in advance

Try conditional aggregation, something like:
SELECT min(id) id,
itemcode,
sum(CASE
WHEN counter = 'Counter-1' THEN
itemamount
ELSE
0
END) count1result,
'Counter-1' countis1,
sum(CASE
WHEN counter = 'Counter-2' THEN
itemamount
ELSE
0
END) count2result,
'Counter-2' countis2
FROM my_table
GROUP BY itemcode;

You can try to use Aggregate function condition to make it.
Here is SQL-server sample:
CREATE TABLE My_Table(
id INT,
ItemCode VARCHAR(50),
ItemAmount INT,
Counter VARCHAR(50)
);
INSERT INTO My_Table VALUES (1, '001', 1 ,'Counter-1');
INSERT INTO My_Table VALUES (2, '001', 1 ,'Counter-2');
INSERT INTO My_Table VALUES (3, '002', 2 ,'Counter-1');
INSERT INTO My_Table VALUES (4, '002', 2 ,'Counter-2');
INSERT INTO My_Table VALUES (5, '002', 1 ,'Counter-2');
Query 1:
select
ROW_NUMBER() OVER(ORDER BY itemCode) id,
itemCode,
SUM(CASE WHEN Counter = 'Counter-1' THEN ItemAmount ELSE 0 END) as 'Count 1 Result',
MAX(CASE WHEN Counter = 'COUNTER-1' THEN Counter END) as 'Count Is 1',
SUM(CASE WHEN Counter = 'Counter-2' THEN ItemAmount ELSE 0 END) as 'Count 2 Result',
MAX(CASE WHEN Counter = 'COUNTER-2' THEN Counter END) as 'Count Is 2'
from
My_Table
GROUP BY
itemCode
Results:
| id | itemCode | Count 1 Result | Count Is 1 | Count 2 Result | Count Is 2 |
|----|----------|----------------|------------|----------------|------------|
| 1 | 001 | 1 | Counter-1 | 1 | Counter-2 |
| 2 | 002 | 2 | Counter-1 | 3 | Counter-2 |

Even though I'm not sure what you are expecting with out disturbing your code I have given the query. Implement in your requirement
;WITH CTE AS (select
itemCode,
(select SUM(ItemAmount) where Counter = 'Counter-1') as 'Count 1 Result',
(select MAX(Counter) where Counter = 'COUNTER-1') as 'Count Is 1',
(select SUM(ItemAmount) where Counter = 'Counter-2') as 'Count 2 Result',
(select MAX(Counter) where Counter = 'COUNTER-2') as 'Count Is 2'
from
My_table
GROUP BY
itemCode,Counter )
Select RANK()OVER ( ORDER BY itemcode)Id,
itemCode,
MAX([Count 1 Result])[Count 1 Result],
MAX([Count Is 1])[Count Is 1],
MAX([Count 2 Result])[Count 2 Result],
MAX([Count Is 2])[Count Is 2]
from CTE
GROUP BY itemCode

For the data retrieval part the following would give your data,
by grouping on ItemCode and Counter:
select
ItemCode,
Counter,
min(Id) as Id,
sum(ItemAmount) as ItemAmount,
from
My_Table
group by ItemCode, Counter
ItemCode Counter Id ItemAmount
======== ========= === ==========
001 Counter-1 1 1
001 Counter-2 1 2
002 Counter-1 3 1
002 Counter-2 3 3
To display that as so called pivot table, rows-to-columns, there are several solutions, one generic SQL solution:
In Modern SQL:
select ItemCode,
ItemAmount filter (where Counter = 'Counter-1') as A1,
ItemAmount filter (where Counter = 'Counter-2') as A2
from (select
ItemCode,
Counter,
min(Id) as Id,
sum(ItemAmount) as ItemAmount,
from
My_Table
group by ItemCode, Counter)
group by ItemCode
If filter is still not supported in the used SQL:
select ItemCode,
sum(case when Counter = 'Counter-1' then ItemAmount end) as A1,
sum(case when Counter = 'Counter-2' then ItemAmount end) as A2,

Related

Aggregation for multiple SQL SELECT statements

I've got a table TABLE1 like this:
|--------------|--------------|--------------|
| POS | TYPE | VOLUME |
|--------------|--------------|--------------|
| 1 | A | 34 |
| 2 | A | 2 |
| 1 | A | 12 |
| 3 | B | 200 |
| 4 | C | 1 |
|--------------|--------------|--------------|
I want to get something like this (TABLE2):
|--------------|--------------|--------------|--------------|--------------|
| POS | Amount_A | Amount_B | Amount_C | Sum_Volume |
|--------------|--------------|--------------|--------------|--------------|
| 1 | 2 | 0 | 0 | 46 |
| 2 | 1 | 0 | 0 | 2 |
| 3 | 0 | 1 | 0 | 200 |
| 4 | 0 | 0 | 1 | 1 |
|--------------|--------------|--------------|--------------|--------------|
My Code so far is:
SELECT
(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'A') AS [Amount_A]
,(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'B') AS [Amount_B]
,(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'C') AS [Amount_C]
,(SELECT SUM(VOLUME)
FROM TABLE AS [Sum_Volume]
INTO [TABLE2]
Now two Questions:
How can I include the distinction concerning POS?
Is there any better way to count each TYPE?
I am using MSSQLServer.
What you're looking for is to use GROUP BY, along with your Aggregate functions. So, this results in:
USE Sandbox;
GO
CREATE TABLE Table1 (Pos tinyint, [Type] char(1), Volume smallint);
INSERT INTO Table1
VALUES (1,'A',34 ),
(2,'A',2 ),
(1,'A',12 ),
(3,'B',200),
(4,'C',1 );
GO
SELECT Pos,
COUNT(CASE WHEN [Type] = 'A' THEN [Type] END) AS Amount_A,
COUNT(CASE WHEN [Type] = 'B' THEN [Type] END) AS Amount_B,
COUNT(CASE WHEN [Type] = 'C' THEN [Type] END) AS Amount_C,
SUM(Volume) As Sum_Volume
FROM Table1 T1
GROUP BY Pos;
DROP TABLE Table1;
GO
if you have a variable, and undefined, number of values for [Type], then you're most likely going to need to use Dynamic SQL.
your first column should be POS, and you'll GROUP BY POS.
This will give you one row for each POS value, and aggregate (COUNT and SUM) accordingly.
You can also use CASE statements instead of subselects. For instance, instead of:
(SELECT COUNT(TYPE)
FROM TABLE1
WHERE TYPE = 'A') AS [Amount_A]
use:
COUNT(CASE WHEN TYPE = 'A' then 1 else NULL END) AS [Amount_A]

SQL Grouping entries with a different value

Let's assume I have a report that displays an ID and VALUE from different tables
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 1 | 1 |
4 | 2 | 0 |
5 | 2 | 0 |
My goal is to display this table with grouped IDs and VALUEs. My rule to grouping VALUEs would be "If VALUE contains atleast one '1' then display '1' otherwise display '0'".
My current SQL is (simplified)
SELECT
TABLE_A.ID,
CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END AS VALUE
FROM TABLE_A, TABLE_B, TABLE_C
GROUP BY
TABLE_A.ID
(CASE
WHEN TABLE_B.VALUE = 1 OR TABLE_C.VALUE NOT IN (0,1,2,3)
THEN 1
ELSE 0
END)
The output is following
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 1 | 0 |
3 | 2 | 0 |
Which is half way to the output I want
| ID | VALUE |
|----|-------|
1 | 1 | 1 |
2 | 2 | 0 |
So my Question is: How do I extend my current SQL (or change it completely) to get my desired output?
If you are having only 0 and 1 as distinct values in FOREIGN_VALUE column then using max() function as mentioned by HoneyBadger in the comment will fulfill your requirement.
SELECT
ID,
MAX(FOREIGN_VALUE) AS VALUE
FROM (SELECT
ID,
CASE WHEN FOREIGN_VALUE = 1
THEN 1
ELSE 0
END AS FOREIGN_VALUE
FROM TABLE,
FOREIGN_TABLE)
GROUP BY
ID;
Assuming value is always 0 or 1, you can do:
select id, max(value) as value
from t
group by id;
If value can take on other values:
select id,
max(case when value = 1 then 1 else 0 end) as value
from t
group by id;

Count each condition within group

For every unique GroupId I would like to get a count of each IsGreen, IsRound, IsLoud condition and a total number of rows.
Sample data:
-----------------------------------------------------
id | ItemId | GroupId | IsGreen | IsRound | IsLoud
----+--------+---------+---------+---------+---------
1 | 1001 | 1 | 0 | 1 | 1
2 | 1002 | 1 | 1 | 1 | 0
3 | 1003 | 2 | 0 | 0 | 0
4 | 1004 | 2 | 1 | 0 | 1
5 | 1005 | 2 | 0 | 0 | 0
6 | 1006 | 3 | 0 | 0 | 0
7 | 1007 | 3 | 0 | 0 | 0
Desired result:
----------------------------------------------------------
GroupId | TotalRows | TotalGreen | TotalRound | TotalLoud
--------+-----------+------------+------------+-----------
1 | 2 | 1 | 2 | 1
2 | 3 | 1 | 0 | 1
3 | 2 | 0 | 0 | 0
I'm using the following code to create the table, the problem I'm having is that if any of the groups have no rows that match one of the conditions that group does not appear in the final table. What is the best way to accomplish what I want to do?
SELECT total.GroupId
, total.[Count] AS TotalRows
, IsGreen.[Count] AS TotalGreen
, IsRound.[Count] AS TotalRound
, IsLoud.[Count] AS TotalLoud
FROM (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
GROUP BY GroupId
) TotalRows
INNER JOIN (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
WHERE IsGreen = 1
GROUP BY GroupId
) IsGreen ON IsGreen.GroupId = TotalRows.GroupId
INNER JOIN (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
WHERE IsRound = 1
GROUP BY GroupId
) IsRound ON IsRound.GroupId = TotalRows.GroupId
INNER JOIN (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
WHERE IsLoud = 1
GROUP BY GroupId
) IsLoud ON IsLoud.GroupId = TotalRows.GroupId
You can use count to count rows per each [GroupId] and sum to count each property .
select [GroupId]
, count([GroupId]) as [TotalRows]
, sum([IsGreen]) as [TotalGreen]
, sum([IsRound]) as [TotalRound]
, sum([IsLoud]) as [TotalLoud]
from [TestData]
group by [GroupId]
Use conditional Aggregate. Try this.
SELECT GroupId,
Count(GroupId) TotalRows,
Count(CASE WHEN IsGreen = 1 THEN 1 END) TotalGreen,
Count(CASE WHEN IsRound = 1 THEN 1 END) TotalRound,
Count(CASE WHEN IsLoud = 1 THEN 1 END) TotalLoud
FROM tablename
GROUP BY GroupId

Increment variable in sql query

Using SQL Server 2008, I want to query a table like so:
| ID | Number
-------------
| 1 | 0
| 2 | 0
| 3 | 1
| 4 | 0
| 5 | 0
| 6 | 1
| 7 | 1
| 8 | 1
The result should be the same table with an additional column that counts.
The method of counting is: if the number in "number" equals to 1 - increment the counter by one for the next line.
An example of result for the provided table:
| ID | Number | Counter
-----------------------
| 1 | 0 | 1
| 2 | 0 | 1
| 3 | 1 | 1
| 4 | 0 | 2
| 5 | 0 | 2
| 6 | 1 | 2
| 7 | 1 | 3
| 8 | 1 | 4
How can this be achieved?
select [ID], [Number],
isnull(1+(select sum([Number]) from Table1 t2 where t2.ID<t1.Id),1)
from Table1 t1
SQL Fiddle to test
This is not too hard to do. What you are looking for is very much like the running total, which you get with sum and a windowing clause.
select id, num, 1 + sum(num) over (order by id) - num as counter
from mytable
order by id;
Here is an SQL fiddle: http://sqlfiddle.com/#!4/958e2a/1.
You can use recursive select too but it is a bit complicated but if you insert other numbers which are greater than 1 it work fine:
with tab(id,number,counter,rn) as
(select t.*,1 as counter,1 as rn from table1 t where id = 1
union all
select t.*,case when t.number = 1 then counter + 1 else counter end as counter,
rn + 1 as rn from table1 t,tab where t.id = tab.rn + 1),
tab2 as (select id,number,counter from tab)
select id,number,case when number = 1 then counter - 1
else counter end as counter from tab2;
SQL Fiddle

Efficent way to generate a summery table in SQL. Please see explanation

I am very new to sql.I need some help in generating summery information
MemberTable
MonthID | UserID | TeamID
-----------------------------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | 4 | 1
1 | 5 | 2
1 | 6 | 2
1 | 7 | 2
AND
ReportTable
ID* | MonthID | UserID | IsSend
-----------------------------------
1 | 1 | 2 | False
2 | 1 | 3 | True
3 | 1 | 5 | True
I want to generate a summery like the following
TeamID | Total Count | Send Count | Not Send Count
-----------------------------------------------------------
1 | 4 | 1 | 3
2 | 3 | 1 | 2
Total Count : No of users in a team
Send Count : Total User in a team with IsSend = True
Not Send Count : Total Count - Send Count
What would be the efficent way?
Give this a try:
select mt.teamId, count(*) totalCount,
count(case when rt.isSend = 'True' then 1 end) sendCount,
count(case when rt.isSend != 'True' then 1 end) notSendCount
from memberTable mt
join reportTable rt on mt.userId = rt.userId
group by mt.teamId
Note that your expected result does not reflect your data. The result based on your data should be:
+--------+------------+-----------+--------------+
| TEAMID | TOTALCOUNT | SENDCOUNT | NOTSENDCOUNT |
+--------+------------+-----------+--------------+
| 1 | 2 | 1 | 1 |
| 2 | 1 | 1 | 0 |
+--------+------------+-----------+--------------+
select MT.TeamID,
count(distinct MT.UserID) as "Total Count",
count(distinct case when RT.IsSend = 1 then MT.UserID end) as "Send Count",
count(distinct MT.UserID) - count(distinct case when RT.IsSend = 1 then MT.UserID end) as "Not Send Count"
from MemberTable as MT
left outer join ReportTable as RT
on MT.MonthID = RT.MonthID and
MT.UserID = RT.UserID
group by MT.TeamID
Result:
TeamID Total Count Send Count Not Send Count
----------- ----------- ----------- --------------
1 4 1 3
2 3 1 2
Try here: https://data.stackexchange.com/stackoverflow/query/66347
Without havign the tables to try this on, I can't check that this will work, but this shoul get you most of the way:
SELECT TeamID, count(userID) as "Total count", Sum(IsSend) as "Send Count" FROM MemberTable JOIN ReportTable ON UserID GROUP BY TeamID;