GROUP BY A, B, C, and else for counting data? - sql

I want to try group by with data with certain types and etc.
CREATE TABLE dbo.T_TEST(
TYPE nchar(1) NOT NULL,
Qty int NOT NULL
)
INSERT INTO [T_TEST] VALUES ('A','2');
INSERT INTO [T_TEST] VALUES ('A','1');
INSERT INTO [T_TEST] VALUES ('A','2');
INSERT INTO [T_TEST] VALUES ('B','22');
INSERT INTO [T_TEST] VALUES ('B','21');
INSERT INTO [T_TEST] VALUES ('C','2');
INSERT INTO [T_TEST] VALUES ('C','2');
INSERT INTO [T_TEST] VALUES ('C','2');
INSERT INTO [T_TEST] VALUES ('C','2');
INSERT INTO [T_TEST] VALUES ('D','2');
INSERT INTO [T_TEST] VALUES ('D','2');
INSERT INTO [T_TEST] VALUES ('D','2');
INSERT INTO [T_TEST] VALUES ('E','1');
INSERT INTO [T_TEST] VALUES ('E','1');
INSERT INTO [T_TEST] VALUES ('E','4');
INSERT INTO [T_TEST] VALUES ('F','5');
The result I am wondering is like this.
TYPE Count
A 3
B 2
C 4
etc 7
I could do with get total count and subtract count A,B,C.
But I believe it's not a good logic, Please help me with this.

You may try the following and see working fiddle:
SELECT
CASE WHEN TYPE IN ('A','B','C') THEN TYPE ELSE 'etc' END as [Type],
COUNT(Type) as [Count]
FROM
T_TEST
GROUP BY
CASE WHEN TYPE IN ('A','B','C') THEN TYPE ELSE 'etc' END
GO
Type | Count
:--- | ----:
A | 3
B | 2
C | 4
etc | 7
db<>fiddle here

You need a basic GROUP BY query:
SELECT TYPE, COUNT(*) AS Count
FROM dbo.T_TEST
GROUP BY TYPE
ORDER BY TYPE;

Related

SQL (PLSQL) number a set of rows

I attended an interview recently and the interviewer asked me number the occurrences of 'A', 'B', 'C' and so on. To put in table and columns - there is a table tab with column as col. The values in col is 'A', 'B', 'C' etc.
create table tab226 (col varchar2(3) );
insert into tab226 VALUES ('A');
insert into tab226 VALUES ('B');
insert into tab226 VALUES ('C');
insert into tab226 VALUES ('B');
insert into tab226 VALUES ('A');
insert into tab226 VALUES ('C');
insert into tab226 VALUES ('C');
insert into tab226 VALUES ('A');
insert into tab226 VALUES ('B');
The expected output is :
Interviewer told me I can use SQL or PLSQL to achieve it. I thought about it for almost 10 mins but couldn't come up with a plan let alone the solution. Does anyone know if this can be achieved in Oracle SQL or PLSQL?
Doesn't make much sense to me, but - would this do?
SQL> select col,
2 count(*) over (partition by col order by rowid) exp_output
3 from tab226
4 order by rowid;
COL EXP_OUTPUT
--- ----------
A 1
B 1
C 1
B 2
A 2
C 2
C 3
A 3
B 3
9 rows selected.
SQL>
As written, you cannot accomplish -- consistently -- what they are asking for. The problem is that SQL tables represent unordered sets. There is no way to run a query on the original data and preserve the ordering.
However, the final column appears to simply be an enumeration, so you can use row_number() for that:
select col, row_number() over (partition by col order by NULL)
from tab226;
But if you have an ordering column -- say id in this example -- then you would do:
select col, row_number() over (partition by col order by NULL)
from tab226;
Here is a db<>fiddle.

Select rows that do not have the opposite value in another row

I am trying to write a query that will only select rows that does not have the opposite value. For example if a column (payments) has 2 negative numbers(-11) and 3 positive numbers(11) both the negative numbers would cancel out and one positive number will remain. I may be explaining this wrong. But any help is appreciated.
table :
CREATE TABLE hamzachecks(
ID VARCHAR(2) NOT NULL
,CHECK VARCHAR(10) NOT NULL
,Payment VARCHAR(8) NOT NULL
);
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('1','9549549544','-112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('2','9549549544','-112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('3','9549549544','112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('4','9549549544','112.96');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('5','9549549544','-165.92');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('6','9549549544','225.92');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('7','9549549544','-299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('8','9549549544','-299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('9','9549549544','-299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('10','9549549544','299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('11','9549549544','299.3');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('12','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('13','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('14','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('15','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('16','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('17','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('18','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('19','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('20','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('21','9549549544','-415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('22','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('23','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('24','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('25','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('26','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('27','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('28','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('29','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('30','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('31','9549549544','415.14');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('32','9549549544','-1024.22');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('33','9549549544','1024.22');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('34','9549549578','-253.77');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('35','9549549578','253.77');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('36','9549549578','-3332.16');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('37','9549549578','-6664.29');
INSERT INTO hamzachecks(ID,CHECK,Payment) VALUES ('38','9549549578','6664.29');
The basic logic is the same as #Marty's, assign a row number to each row in with the same chk/payment combination.
SELECT chk
,id
,Payment
,Row_Number() Over (PARTITION BY chk, Payment ORDER BY Id) AS rn
FROM hamzachecks
order by
chk
-- remove the leading '-'', for numeric data: abs(Payment)
,substring(Payment, charindex('-', Payment)+1, 8000)
,rn
,Payment
It there's a matching row both rows will share the same rn
...
9549549544 3 112.96 1 -- 1st group
9549549544 1 -112.96 1 -- matching value: remove
9549549544 4 112.96 2 -- 2nd group
9549549544 2 -112.96 2 -- matching value: remove
...
9549549544 10 299.3 1 -- 1st group
9549549544 7 -299.3 1 -- matching value: remove
9549549544 11 299.3 2 -- 2nd group
9549549544 8 -299.3 2 -- matching value: remove
9549549544 9 -299.3 3 -- 3rd group, no matching value: keep
...
Now remove the groups with two rows using aggregation:
;WITH cte AS
(
SELECT chk
,id
,Payment
,Row_Number() Over (PARTITION BY chk, Payment ORDER BY Id) AS rn
FROM hamzachecks
)
SELECT
Min(id)
,chk
,max(Payment)
FROM cte
GROUP BY
chk
-- remove the leading '-'', for numeric data: abs(Payment)
,substring(Payment, charindex('-', Payment)+1, 8000)
,rn
HAVING count(*) = 1
See fiddle
I'm going to ignore possible considerations for why you want to do this particular job in this particular way.
So basically: the following SQL will give you the result you are asking for...
;with b (id, payment, rn) as
(select id, payment, row_number() over (partition by Payment order by Id) as rn
from hamzachecks
where payment < 0),
a (id, payment, rn) as
(select id, payment, row_number() over (partition by Payment order by Id) as rn
from hamzachecks
where payment > 0),
matched_ids (id1,id2) as
(select a.id id1, b.id id2
from a
join b on a.payment = -b.payment and a.rn = b.rn)
select *
from hamzachecks
where id not in (select id1 from matched_ids union select id2 from matched_ids)
order by id
This needs a change in your schema: define the Payment column as MONEY (or other number type) instead of VARCHAR(8).
This would be a starting point for further work, as this solution is ignoring the CHECK column.
Explanation: first we split the source table into two groups: a - above zero (payment > 0) and b - below zero (payment < 0). Both of those groups will get another column row_number() ... that get gets each row an "rowID" - based on the position in the particular group (partition by Payment) that basically says: "This is n-th row with the same Payment". The join part then matches all rows that have the same (but opposite) Payment in the other group and also checks the rowID - we now know all rows that have an opposite value. So the main select just ignores those rows and return only those that don't have an opposite value.

How to select convert and sum from database?

I have a column price datatype is nvarchar because I save thousand seperator with comma like 1,000,000.00. I now need to select convert and sum it in select statement then display group by id in crystalreport.
My select statement:
select id,productname,type,price,datesell,seller from tb_billdetail group by id
Here I need how to convert the price from nvarchar to type double and sum it after converted but just using select statement?
Something I need to display in crystal report like:
col_id col_name col_type col_price col_date
**001**
001 fish food 20,000.00 01/02/2018
001 fish food 10,000.00 10/02/2018
col_sum id 001: 30,000.00
next group id 002 and 003 ....
Declare #tt table (name nvarchar(10), value nvarchar(10))
insert into #tt (name, value) values ('a', '1,000')
insert into #tt (name, value) values ('a', '1,10,000')
insert into #tt (name, value) values ('b', '2,000.4223')
insert into #tt (name, value) values ('b', '4,000.2')
select name, sum(cast(REPLACE(value,',','') as float)) as sum from #tt group by name
it's not a good idea to store with comma separate, store it's as the decimal.
If your storing comma for displaying purpose then add it when displaying.

Sum data from one table if date range is in another table

In SQL Server I have two query results like this:
I would like to use this 2 queries and then SUM the Seconds of the second table (Mains - Charging) if that specific device (43 in this case) was connected in the date range of the first table for that period using the left and right date limits. So SUM only if that condition is met.
Any suggestion on how I can accomplish this ?
This is the table script and some sample data:
First table script:
CREATE TABLE firstTable(
DeviceId INTEGER NOT NULL
,Date DATE NOT NULL
,DisplayValue VARCHAR(16) NOT NULL
,LeftLimit DATETIME NOT NULL
,RigthLimit DATETIME NOT NULL
,Seconds INTEGER NOT NULL
);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 00:00:00.000','2017-06-27 10:17:54.460',37074);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:54.460','2017-06-27 10:17:56.293',2);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:56.293','2017-06-27 10:17:56.330',0);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:56.330','2017-06-27 10:17:57.430',1);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:57.430','2017-06-27 10:17:57.440',0);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:57.440','2017-06-27 10:17:58.637',1);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:58.637','2017-06-27 10:17:58.783',0);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:58.783','2017-06-27 10:17:59.657',1);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:59.657','2017-06-27 10:17:59.903',0);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:17:59.903','2017-06-27 10:18:00.977',1);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:18:00.977','2017-06-27 10:18:01.027',1);
INSERT INTO firstTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Connected','2017-06-27 10:18:01.027','2017-06-27 10:18:01.517',0);
Second table script:
CREATE TABLE secondTable(
DeviceId INTEGER NOT NULL
,Date DATE NOT NULL
,DisplayValue VARCHAR(16) NOT NULL
,LeftLimit DATETIME NOT NULL
,RigthLimit DATETIME NOT NULL
,Seconds INTEGER NOT NULL
);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 00:00:00.000','2017-06-27 10:17:57.523',37077);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 10:17:57.523','2017-06-27 10:17:59.883',2);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 10:17:59.883','2017-06-27 10:17:59.953',0);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 10:17:59.953','2017-06-27 10:18:01.043',2);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 10:18:01.043','2017-06-27 10:18:01.103',0);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 10:18:01.103','2017-06-27 10:18:01.553',0);
INSERT INTO secondTable(DeviceId,Date,DisplayValue,LeftLimit,RigthLimit,Seconds) VALUES (43,'2017-06-27','Mains - Charging','2017-06-27 10:18:01.553','2017-06-27 10:18:01.630',0);
Expected Result:
CREATE TABLE expectedResult(
DeviceId INTEGER NOT NULL
,LeftLimit DATETIME NOT NULL
,RigthLimit DATETIME NOT NULL
,TotalSeconds INTEGER NOT NULL
);
INSERT INTO expectedResult(DeviceId,LeftLimit,RigthLimit,TotalSeconds) VALUES (43,'2017-06-27 00:00:00.000','2017-06-27 10:18:02.630',37081);
Expected Result table:
+----------+-------------------------+-------------------------+--------------+
| DeviceId | LeftLimit | RightLimit | TotalSeconds |
+----------+-------------------------+-------------------------+--------------+
| 43 | 2017-06-27 00:00:00.000 | 2017-06-27 10:18:02.630 | 37081 |
+----------+-------------------------+-------------------------+--------------+
SELECT DISTINCT
ST.[DeviceId],
MIN(CASE WHEN FT.[LeftLimit] > ST.[LeftLimit] THEN FT.[LeftLimit] ELSE ST.[LeftLimit] END) AS [LeftLimit],
MAX(CASE WHEN FT.[RigthLimit] < ST.[RigthLimit] THEN FT.[RigthLimit] ELSE ST.[RigthLimit] END) AS [RigthLimit],
SUM(DATEDIFF(
SECOND,
CASE WHEN FT.[LeftLimit] > ST.[LeftLimit] THEN FT.[LeftLimit] ELSE ST.[LeftLimit] END,
CASE WHEN FT.[RigthLimit] < ST.[RigthLimit] THEN FT.[RigthLimit] ELSE ST.[RigthLimit] END)) AS [TotalSeconds]
FROM firstTable FT
INNER JOIN secondTable ST ON ST.[DeviceId] = FT.[DeviceId] AND (FT.[LeftLimit] <= ST.[RigthLimit] AND ST.[LeftLimit] <= FT.[RigthLimit])
GROUP BY ST.[DeviceId]
Join the first table on the second, but only where the date ranges overlap. Then take the min left and max right limit and sum.
JOIN the two tables ON the LeftLimit from the 2nd table being greater than the MIN LeftLimit from the 1st table, AND the RightLimit from the 2nd table being less than the MAX RightLimit from the 1st table. And also on DeviceId of course.
You'll also need to CAST all your limit columns as datetimes so that they compare correctly.

How can I JOIN two tables columns using a wildcard?

I'm using Access 97.
This is what I wanna do:
SELECT *
FROM CodeDIS, ListOfProducts
WHERE CodeDIS.IDNumber LIKE ListOfProducts.IDNumber*;
Sometimes the IDNumber on ListOfProducts has an extra letter indicating some information about the product.
So I have the IDNumber AC244 on CodeDIS but I have AC244P on ListOfProducts.
All I want is to add a wildcard to the end of the condition.
Is this possible?
If the choice is C or P, why not:
SELECT *
FROM CodeDIS, ListOfProducts
WHERE CodeDIS.IDNumber = ListOfProducts.IDNumber & "C"
OR CodeDIS.IDNumber = ListOfProducts.IDNumber & "P"
How about:
SELECT *
FROM CodeDIS, ListOfProducts
WHERE CodeDIS.IDNumber LIKE left(ListOfProducts.IDNumber,5);
or if you like:
SELECT *
FROM CodeDIS a inner join ListOfProducts b on a.IDNumber = left(b.IDNumber,5);
I know this is old, but I have not posted much if ever and thought I would try here.
Why not use a substring function with PATINDEX testing if the last digit is always a character? I'm sure my code is mega-inefficient, but here goes my beginner attempt. I'll bet some could golf my case statement into something smaller.
-- I used the code below
-- It only works if the final digit is lower or uppercase letter of
-- specified language.
DECLARE #CodeDIS TABLE (IdNumber nvarchar(50), BaseRowNum nvarchar(50));
insert into #CodeDIS (IdNumber,BaseRowNum) values ('52352345','1')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('134131','2')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('1343141','3')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('a143321','4')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('c34324','5')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('1343214','6')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('%134324','7')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('a%134324','8')
insert into #CodeDIS (IdNumber,BaseRowNum) values ('1413','9')
;
DECLARE #ListOfProducts TABLE (IdNumber nvarchar(50), UpdateSourceDataColumn nvarchar(50));
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('52352345a','11')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('134131','22')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('1343141a','33')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('a143321','44')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('c343245','55')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('1343214Z','66')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('%134324','77')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('a%134324','88')
insert into #ListOfProducts (IdNumber,UpdateSourceDataColumn) values ('1413\','99')
;
SELECT a.*,b.IdNumber as IdNumber2, b.UpdateSourceDataColumn
FROM #CodeDIS a
left join #ListOfProducts b
on a.IdNumber =
case
when PATINDEX(
'[a-zA-Z]',
substring(b.IdNumber,len(b.IdNumber),1)
COLLATE Latin1_General_CS_AS
)=1
then left(b.IdNumber,len(b.IdNumber)-1)
else b.IdNumber
end
;
--IdNumber BaseRowNum IdNumber2 UpdateSourceDataColumn
--52352345 1 52352345a 11
--134131 2 134131 22
--1343141 3 1343141a 33
--a143321 4 a143321 44
--c34324 5 NULL NULL
--1343214 6 1343214Z 66
--%134324 7 %134324 77
--a%134324 8 a%134324 88
--1413 9 NULL NULL