SELECT and COUNT data in a specific range - sql

I would like to check all records for a certain range (1-10) and output the quantity. If there is no record with the value in the database, 0 should also be output.
Example database:
CREATE TABLE exampledata (
ID int,
port int,
name varchar(255));
Example data:
INSERT INTO exampledata
VALUES (1, 1, 'a'), (2, 1, 'b'), (3, 2, 'c'), (4, 2, 'd'), (5, 3, 'e'), (6, 4, 'f'), (7, 8, 'f');
My example query would be:
SELECT
port,
count(port) as amount
FROM exampledata
GROUP BY port
Which would result in:
port
amount
1
2
2
2
3
1
4
1
8
1
But I need it to look like that:
port
amount
1
2
2
2
3
1
4
1
5
0
6
0
7
0
8
1
9
0
10
0
I have thought about a join with a database that has the values 1-10 but this does not seem efficient. Several attempts with different case and if structures were all unsuccessful...
I have prepared the data in a db<>fiddle.

This "simple" answer here would be to use an inline tally. As you just want the values 1-10, this can be achieved with a simple VALUES table construct:
SELECT V.I AS Port,
COUNT(ed.ID) AS Amount
FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))V(I)
LEFT JOIN dbo.exampledata ed ON V.I = ed.port
GROUP BY V.I;
Presumably, however, you actually have a table of ports, and so what you should be doing is LEFT JOINing from that:
SELECT P.PortID AS Port,
COUNT(ed.ID) AS Amount
FROM dbo.Port P
LEFT JOIN dbo.exampledata ed ON P.PortID = ed.port
WHERE P.PortID BETWEEN 1 AND 10
GROUP BY V.I;
If you don't have a table of ports (why don't you?), and you need to parametrise the values, I suggest using a actual Tally Table or Tally function; a search of these will give you a wealth of resources on how to create these.

Related

Get last value based on a condition on ordered table in SQL

I have a table representing product groups. Groups are defined by row number and their Type. Hence here we have 2 groups, the group is defined by the Type A, the Type B being components.
Row number
Type
0
A
1
B
2
B
3
A
4
B
5
B
With just this data, I need to find back the row number of the last preceeding Type A occurence :
Row number
Type
Row father
0
A
0
1
B
0
2
B
0
3
A
3
4
B
3
5
B
3
I can't find a way to get this. It's a sort of lag() or first_value() based on condition. Here I have 2 groups of 2 components, but I can have more groups with different sizes. The only thing that defines the group are the row number : every row number under Type A (so with Type B) is a child of the above Type A, until next type A.
Thank you for your help
You can achieve the desired result by using a join in conjunction with a group-by.
Test Data
CREATE TABLE TEST (ROW_NUMBER integer, TYPE varchar(1));
INSERT INTO test VALUES (0, 'A');
INSERT INTO test VALUES (1, 'B');
INSERT INTO test VALUES (2, 'B');
INSERT INTO test VALUES (3, 'A');
INSERT INTO test VALUES (4, 'B');
INSERT INTO test VALUES (5, 'B');
Query
SELECT T.*, MAX(A.ROW_NUMBER) AS ROW_FATHER
FROM TEST T
LEFT JOIN TEST A
ON A.TYPE = 'A' AND a.ROW_NUMBER <= T.ROW_NUMBER
GROUP BY T.ROW_NUMBER, T.TYPE
ORDER BY T.ROW_NUMBER
Result

Add missing rows within a table

I need a hint please, in my table it can happen that positions of an order is not written to the next ID.
Let's look at the table:
Pos 2 is missing in ID 3
ID
DOC
POSI
TOTAL
1
123
1
100
1
123
2
600
1
123
3
200
2
123
1
100
2
123
2
600
2
123
3
200
3
123
1
100
3
123
3
200
Is it possible to create a view using SQL that compares the individual IDs partitions with each other and appends the missing value from ID 2 to ID 3 as a row?
Maybe you have some keywords for me, if something like this is possible.
The hint would be: Use a join.
One way of approaching this is, that you select the key pairs that you expect and then left join the original table. Be conscious about the missing-value handling, since you have not specified in your question what should happen to those newly created entries.
Test Data
CREATE TABLE test (id INTEGER, doc INTEGER, posi INTEGER, total INTEGER);
INSERT INTO test VALUES (1, 123, 1, 100);
INSERT INTO test VALUES (1, 123, 2, 600);
INSERT INTO test VALUES (1, 123, 3, 200);
INSERT INTO test VALUES (2, 123, 1, 100);
INSERT INTO test VALUES (2, 123, 2, 600);
INSERT INTO test VALUES (2, 123, 3, 200);
INSERT INTO test VALUES (3, 123, 1, 100);
INSERT INTO test VALUES (3, 123, 3, 200);
The possible key combinations can be generated with a cross join:
SELECT DISTINCT a.id, b.posi
FROM test a, test b
And now join the original table:
WITH expected_lines AS (
SELECT DISTINCT a.id, b.posi
FROM test a, test b
)
SELECT el.id, el.posi, t.doc, t.total
FROM expected_lines el
LEFT JOIN test t ON el.id = t.id AND el.posi = t.posi
You did not describe further, what should happen with the now empty columns. As you may note DOC and TOTAL are null.
My educated guess would be, that you want to make DOC part of the key and assume a TOTAL of 0. If that's the case, you can go with the following:
WITH expected_lines AS (
SELECT DISTINCT a.id, b.posi, c.doc
FROM test a, test b, test c
)
SELECT el.id, el.posi, el.doc, ifnull(t.total, 0) total
FROM expected_lines el
LEFT JOIN test t ON el.id = t.id AND el.posi = t.posi AND el.doc = t.doc
Result

SQL, order by data entered

I wasn't quite sure how to word this question. But if you imagine I have:
var contents = "5, 7, 1, 3, 4";
And I want to do a query:
SELECT id,name FROM db WHERE id in (contents);
I would get the following response (a):
ID NAME
1 ONE
3 THREE
4 FOUR
5 FIVE
7 SEVEN
But in reality, I want it to be ordered by the order of contents, i.e (b):
ID NAME
5 FIVE
7 SEVEN
1 ONE
3 THREE
4 FOUR
Is there anyway to have the resposne ordered as b and not a
An ANSI SQL method uses a giant case expression:
SELECT id,name
FROM db
WHERE id in (contents)
ORDER BY (CASE id WHEN 5 THEN 1 WHEN 7 THEN 2 WHEN 1 THEN 3 WHEN 3 THEN 4 WHEN 4 THEN 5 END);
With standard ANSI SQL you could join to a list of values that specify the sort order:
select t.*
from the_table t
join (
values
(5, 1),
(7, 2),
(1, 3),
(3, 4),
(4, 5)
) as s (id, sort_order) on t.id = s.id
order by s.sort_order ;
Online example: http://rextester.com/UDW37167

Querying SQL Server table with different values in same column with same ID [duplicate]

This question already has answers here:
Querying SQL table with different values in same column with same ID
(2 answers)
Closed 6 years ago.
I have an SQL Server 2012 table with ID, First Name and Last name. The ID is unique per person but due to an error in the historical feed, different people were assigned the same id.
------------------------------
ID FirstName LastName
------------------------------
1 ABC M
1 ABC M
1 ABC M
1 ABC N
2 BCD S
3 CDE T
4 DEF T
4 DEF T
There are two ID's which are present multiple time. 1 and 4. The rows with id 4 are identical. I dont want this in my result. The rows with ID 1, although the first name is same, the last name is different for 1 row. I want only those ID's whose ID is same but one of the first or last names is different.
I tried loading ID's which have multiple occurrences into a temp table and tried to compare it against the parent table albeit unsuccessfully. Any other ideas that I can try and implement?
This is the output I am looking for
ID
---
1
If you want the ids, then use aggregation and having:
select id
from t
group by id
having min(firstname) <> max(firstname) or min(lastname) <> max(lastname);
Try This:
CREATE TABLE #myTable(id INT, firstname VARCHAR(50), lastname VARCHAR(50))
INSERT INTO #myTable VALUES
(1, 'ABC', 'M'),
(1, 'ABC', 'M'),
(1, 'ABC', 'M'),
(1, 'ABC', 'N'),
(2, 'BCD', 'S'),
(3, 'CDE', 'T'),
(4, 'DEF', 'T'),
(4, 'DEF', 'T')
SELECT id FROM (
SELECT DISTINCT id, firstname, lastname
FROM #myTable) t GROUP BY id HAVING COUNT(*)>1
OUTPUT is : 1

How to Get Sum of One Column Based On Other Table in Sql Server

I have 2 table in my database (like this):
tblCustomers:
id CustomerName
1 aaa
2 bbb
3 ccc
4 ddd
5 eee
6 fff
tblPurchases:
id CustomerID Price
1 1 300
2 2 100
3 3 500
4 1 150
5 4 50
6 3 250
7 6 700
8 2 30
9 1 310
10 4 25
Now I want with "Stored Procedures" take a new table that give me the sum of price for each customer. Exactly like under.
How can do that?
Procedures Result:
id CustomerName SumPrice
1 aaa 760
2 bbb 130
3 ccc 750
4 ddd 75
5 eee 0
6 fff 700
select c.id, c.customername, sum(isnull(p.price, 0)) as sumprice
from tblcustomers c
left join tblpurchases p
on c.id = p.customerid
group by c.id, c.customername
SQL Fiddle test: http://sqlfiddle.com/#!3/9b573/1/0
Note the need for an outer join because your desired result includes customers with no purchases.
You can use the below query to get the result
select id,CustomerName,sum(price) as TotalPrice
from
(
select tc.id,tc.CustomerName,tp.price
from tblCustomers tc
join
tblPurchases tp on tc.id = tp.CustomerID
) tab
group by id,CustomerName
Although the other answers here do work, they don't appear to be what I would consider standard practice, or optimal.
The simplest solution (standard, but not always optimal) requires no sub-query of any variety.
SELECT
cust.id,
cust.CustomerName,
SUM(prch.price) AS SumPrice
FROM
tblCustomers AS cust
INNER JOIN
tblPurchases AS prch
ON cust.id = prch.CustomerID
GROUP BY
cust.id,
cust.CustomerName
The only reason that this is not necessarily optimal is that it involves grouping by two fields, one of which is a string. This involves creating 'counters' in memory that are identified by this composite of an id and string, which can be inefficient due to the fact that you only really need to use the id to uniquely identify the counter. (The identifier is only one item and is a small (probably only 4 bytes), rather than multiple items one of which is long (potentially many many bytes)).
This means that you can do the following as a possible optimisation. Though depending on your data this many be a premature optimsation, it has no performance down-side and is always good to know about...
SELECT
cust.id,
cust.CustomerName,
prch.SumPrice
FROM
tblCustomers AS cust
INNER JOIN
(
SELECT
CustomerID,
SUM(price) AS SumPrice
FROM
tblPurchases
GROUP BY
CustomerID
) AS prch
ON cust.id = prch.CustomerID
This makes the in-memory aggregation as simple as possible, as so as quick as possible.
In both cases you should have the best possible efficiency in the query by ensuring that you have indexes on tblCustomer(id) and on tblPurchases(CustomerID),
DECLARE #tblcustomers table (id int, customername varchar(10));
insert into #tblcustomers values (1, 'aaa');
insert into #tblcustomers values (2, 'bbb');
insert into #tblcustomers values (3, 'ccc');
insert into #tblcustomers values (4, 'ddd');
insert into #tblcustomers values (5, 'eee');
insert into #tblcustomers values (6, 'fff');
DECLARE #tblpurchases table (id int, customerid int, price int);
insert into #tblpurchases values (1, 1, 300);
insert into #tblpurchases values (2, 2, 100);
insert into #tblpurchases values (3, 3, 500);
insert into #tblpurchases values (4, 1, 150);
insert into #tblpurchases values (5, 4, 50);
insert into #tblpurchases values (6, 3, 250);
insert into #tblpurchases values (7, 6, 700);
insert into #tblpurchases values (8, 2, 30);
insert into #tblpurchases values (9, 1, 310);
insert into #tblpurchases values (10, 4, 25);
WITH CTE AS(
select c.id,c.customername from #tblcustomers c
)
Select c.id,c.customername,(Select SUM(ISNULL(P.price,0)) from #tblpurchases P
WHERE P.customerid = C.id) AS Price from CTE c