How to concat all values associated with a key? - sql

I have the following schema:
CREATE TABLE table1
(
user,
phoneType, --from ['A','B','C','D', 'E'], user can have any number of any type
uniquePhoneID, --unique string identifying the phone
day_id --date; record does not necessarily exist for every seen user + phoneType every day, represented as number in example
);
INSERT INTO table1
VALUES (1, 'A', xyz, 1),
(1, 'A', abc, 1),
(1, 'B', def, 2),
(1, 'A', xyz, 2),
(1, 'C', hij, 4),
(1, 'A' xyz, 5),
(2, 'C', w, 9),
(2, 'D', z, 10),
(2, 'A', p, 10),
(2, 'E', c, 11),
(3, 'A', r, 19),
(3, 'B', q, 19),
(3, 'B', q, 20),
(3, 'B', f, 20),
(3, 'B', y, 21);
A single user, uniquePhoneID, day_id will only show up at most once, but not necessarily at all on any given day.
I am looking to concatenate each user in the table with their 4 phoneTypes in alphabetical order, so the result is as follows:
1 | AABC
2 | ACDE
3 | ABBB
I have tried a few different ways of doing this but I am unsure how to get the answer I am looking for.

I think user is a reserved word, so you will have to resolve that. Otherwise, I think something like this will work for you:
select user, string_agg (phonetype, '' order by phonetype)
from table1
group by user
-- EDIT 4/21/2022 --
Aah, okay. I did not glean that from the original question.
What if you used the distinct on the original table before the aggregation?
select userid, string_agg (phonetype, '' order by phonetype)
from (select distinct userid, phonetype, uniquephoneid from table1) x
group by userid
I got these results from this version:
1 AABC
2 ACDE
3 ABBB
If that logic still doesn't work, can you alter the sample data to find an example where it fails?

Related

How to write a running total based on criteria in T-SQL

I'm building a report which gives me the total count of unique accounts within a calendar month.
However, this total is based on the number of active accounts (accounts subscribed to a service), and once their contract ends they will be excluded from the total count.
For example, Company A has subscribed to the service on 1/1/2018 and their contract ends on 1/1/2020. So Company A should be included in the total count of unique accounts for all the months their under contract until their contract ends.
End Result would look something like this:
Here is the SQl query that I have so far. How can I write the code such that it will give me this cumulative/running total. I added the columns for reference.
SELECT A.Name, CA.Name, CA.Start_Date__c, CA.End_Date__c, CA.Product_Code_CPQ__c
FROM [salesforce].[Client_Asset__c] AS CA
INNER JOIN salesforce.Account AS A
ON CA.Account__c = A.Id
WHERE Product_Code_CPQ__c IN(
'DSWPSTRSUB','DSWPESSSUB','DSWPPROSUB','DSWPHOSTSUB','DSWPMULTIHOSTSUB','DSWPOLXWRAPFPE',
'DSWPOLXWRAPSUB','WPCALENDARFORALT','WPCALHOSTINGBUN','IMWPTM','SBWPRET','SBWPRETNR','WORDPLUMWEBSUCCESS',
'WORDPWEBSUCCESS','WORDPOGS','FDSTRWORDPDESGNSUB','FDWPFPE','WORDPEMERGHOST','WORDPSUBBUN','WPOLXPLUGIN',
'POSTSTARTWORDPAF','POSTWORDPSTARTBUN','LUMWORDPSSUBBUN','WORDPLUMOGS','LUMFDSTRWPDESGNSUB',
'LUMPSTWORDPSTRBUN','LUMPOSTSTRTWORDPAF','FDWPEMERGFPE')
AND End_Date__c > GETDATE()
AND Active__c = 1
Try something like that:
CREATE TABLE #tmp ([month] INT, [group] VARCHAR(10), [value] REAL)
INSERT INTO #tmp ([month], [group], [value]) VALUES
(1, 'A', 1), (2, 'A', 5), (3, 'A', 3), (4, 'A', 2), (5, 'A', 8),
(1, 'B', 7), (2, 'B', 3), (3, 'B', 2), (4, 'B', 4), (5, 'B', 6)
SELECT c.[month], c.[group], c.current_total, r.running_total
FROM
(
SELECT [month],[group], SUM([value]) current_total
FROM #tmp
GROUP BY [month],[group]
) C JOIN
(
SELECT [month],[group], SUM([value]) OVER (partition BY [group] ORDER BY [month]) running_total
FROM #tmp
) R ON C.[month]=R.[month] AND C.[group]=R.[group]
ORDER BY 2,1
Tested on mssql 2016. Handle potential missing values yourself.

Find first subset where sum of row group equals given value

I have table in PostgreSQL:
CREATE TABLE test (
id VARCHAR PRIMARY KEY,
name VARCHAR NOT NULL,
amount INTEGER NOT NULL
);
I want SQL to give first combination of tests rows where names are equals and sum of amount test group with same name are equal to a given value
In other words.
If i have values
INSERT INTO test VAlUES
('a', 'name1', 1),
('b', 'name1', 2),
('c', 'name1', 3),
('d', 'name2', 1),
('e', 'name2', 2),
('f', 'name2', 5);
and want find all ids of tests where sum equal 3 the output should be
a,
b,
d,
e
or
c,
d,
e

Select duplicate persons with duplicate memberships

SQL Fiddle with schema and my intial attempt.
CREATE TABLE person
([firstname] varchar(10), [surname] varchar(10), [dob] date, [personid] int);
INSERT INTO person
([firstname], [surname], [dob] ,[personid])
VALUES
('Alice', 'AA', '1/1/1990', 1),
('Alice', 'AA', '1/1/1990', 2),
('Bob' , 'BB', '1/1/1990', 3),
('Carol', 'CC', '1/1/1990', 4),
('Alice', 'AA', '1/1/1990', 5),
('Kate' , 'KK', '1/1/1990', 6),
('Kate' , 'KK', '1/1/1990', 7)
;
CREATE TABLE person_membership
([personid] int, [personstatus] varchar(1), [memberid] int);
INSERT INTO person_membership
([personid], [personstatus], [memberid])
VALUES
(1, 'A', 10),
(2, 'A', 20),
(3, 'A', 30),
(3, 'A', 40),
(4, 'A', 50),
(4, 'A', 60),
(5, 'T', 70),
(6, 'A', 80),
(7, 'A', 90);
CREATE TABLE membership
([membershipid] int, [memstatus] varchar(1));
INSERT INTO membership
([membershipid], [memstatus])
VALUES
(10, 'A'),
(20, 'A'),
(30, 'A'),
(40, 'A'),
(50, 'T'),
(60, 'A'),
(70, 'A'),
(80, 'A'),
(90, 'T');
There are three tables (as per the fiddle above). Person table contains duplicates, same people entered more than once, for the purpose of this exercise we assume that a combination of the first name, surname and DoB is enough to uniquely identify a person.
I am trying to build a query which will show duplicates of people (first name+surname+Dob) with two or more active entries in the Person table (person_membership.person_status=A) AND two or more active memberships (membership.mestatus=A).
Using the example from SQL Fiddle, the result of the query should be just Alice (two active person IDs, two active membership IDs).
I think I'm making progress with the following effort but it looks rather cumbersome and I need to remove Katie from the final result - she doesn't have a duplicate membership.
SELECT q.firstname, q.surname, q.dob, p1.personid, m.membershipid
FROM
(SELECT
p.firstname,p.surname,p.dob, count(*) as cnt
FROM
person p
GROUP BY
p.firstname,p.surname,p.dob
HAVING COUNT(1) > 1) as q
INNER JOIN person p1 ON q.firstname=p1.firstname AND q.surname=p1.surname AND q.dob=p1.dob
INNER JOIN person_membership pm ON p1.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
WHERE pm.personstatus = 'A' AND m.memstatus = 'A'
Since you are using SQL Server windows function will be handy for this scenario. The following will give you the expected output.
SELECT firstname,surname,dob,personid,memberid
from(
SELECT firstname,surname,dob,p.personid,memberid
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid) rnasc
,Rank() over(partition by p.firstname,p.surname,p.dob order by p.personid desc) rndesc
FROM [StagingGRG].[dbo].[person] p
INNER JOIN person_membership pm ON p.personid=pm.personid
INNER JOIN membership m ON pm.memberid = m.membershipid
where personstatus='A' and memstatus='A')a
where a.rnasc+rndesc>2
You have to add Group by and Having clause to return duplicate items only-
SELECT
person.firstname,person.surname,person.dob
FROM
person, person_membership, membership
WHERE
person.personid=person_membership.personid AND person_membership.memberid = membership.membershipid
AND
person_membership.personstatus = 'A' AND membership.memstatus = 'A'
GROUP BY
person.firstname,person.surname,person.dob
HAVING COUNT(1) > 1

Compare BOM Quantity on the Same Table with SQL Server 2012

I am trying to get the comparison Bill of Materials between 2 products in the same table. I have a BOM Table
I would like to compare quantity from
PRODUCT A FROM ID=1 to PRODUCT CCC FROM ID=2
SUPPLIERPN are unique for (ID, PRODUCT) combination
The expected result
My question: How can I get the expected result with SQL?
My SQL Server syntax knowledge to basic queries so I have no idea how to do this. I tried to search but I do not know the correct term to describe this type of problem.
Normally, I solved this type of problem in the application site (loaded all the data from Database Server then did the comparison).
There is no (CCC, 555) so the expected output of that product is incorrect. Honestly, if you can't do this manually and get the correct output you have a much bigger problem. Moreover, posting images is a terrible TERRIBLE way to provide information.
But here is one interpretation that attempts to correct all of your errors. It only occurred to me after writing it that your pn is a string and not numeric - which is why the literals used to populate the table variable do not have quote marks.
set nocount on;
declare #x table (salesid tinyint, product varchar(3), supplierpn varchar(5), qty decimal(5,2));
insert #x (salesid, product, supplierpn, qty) values
(1, 'A', 1234, 1), (1, 'A', 555, 2), (1, 'A', 666, 3),
(1, 'BBB', 1234, 4), (1, 'BBB', 555, 5),
(2, 'CCC', 1234, 6), (2, 'CCC', 666, 7), (2, 'CCC', 777, 8), (2, 'CCC', 888, 9) ;
select * from #x order by salesid, product, qty;
with cte_a as (select supplierpn, qty from #x where salesid = 1 and product = 'A'),
cte_ccc as (select supplierpn, qty from #x where salesid = 2 and product = 'CCC')
select isnull(cte_a.supplierpn, cte_ccc.supplierpn) as supplierpn,
cte_a.qty as qty_a,
cte_ccc.qty as qty_ccc,
isnull(cte_ccc.qty, 0) - isnull(cte_a.qty, 0) as delta
from cte_a full join cte_ccc on cte_a.supplierpn = cte_ccc.supplierpn
order by supplierpn;

Retrieving consecutive rows (and the counts) with the same values

I've got a table with almost 10 million views and would to run this query on the latest million or hundred thousand or so.
Here's a SQL fiddle with example data and input/output: http://sqlfiddle.com/#!9/340a41
Is this even possible?
CREATE TABLE object (`id` int, `name` varchar(7), `value` int);
INSERT INTO object (`id`, `name`, `value`)
VALUES
(1, 'a', 1),
(2, 'b', 2),
(3, 'c', 100),
(4, 'a', 1),
(5, 'b', 2),
(6, 'c', 200),
(7, 'a', 2),
(8, 'b', 2),
(9, 'c', 300),
(10, 'a', 2),
(11, 'b', 2),
(12, 'a', 2),
(13, 'b', 2),
(14, 'c', 400)
;
-- Want:
-- name, max(id), count(id)
-- 'a', 4, 2
-- 'b', 14, 5
-- 'a', 12, 3
If you want the latest and the id is implemented sequentially, then you can do this using limit or top. In SQL Server:
select top 100000 o.*
from object o
order by id desc;
In MySQL, you would use limit:
select o.*
from object o
order by id desc
limit 100000
select name, count(id) cnt, max(id) max_id, max(value) max_v
from
(select
top 1000000 -- MS SQL Server
id,name,value
from myTable
limit 1000000 --mySQL
order by id desc)
group by name
remove line which doesn't match your server.