postgresql & json - counting distinct values - sql

In PostgreSQL, I have a table that looks like,
| id | json |
| -- | ------------------------------- |
| 1 | {"id":1,"customer":"BANK"} |
| 1 | {"id":1,"customer":"BANK"} |
| 2 | {"id":2,"customer":"GOVT"} |
| 3 | {"id":3,"customer":"BANK"} |
| 4 | {"id":4,"customer":"ASSET MGR"} |
| 4 | {"id":4,"customer":"ASSET MGR"} |
I need the output of counting the occurrences of customers with unique ids, such as
| customer | count |
| ----------- | ----- |
| "BANK" | 2 |
| "GOVT" | 1 |
| "ASSET MGR" | 1 |
Is there a good way to achieve using PostgreSQL & json? I currently am able to extract the customer and IDs, but am having difficulty counting the unique json objects.

select count(distinct id), jsondata ->> 'customer' customer
from data
group by customer
count | customer
----: | :--------
1 | ASSET MGR
2 | BANK
1 | GOVT
db<>fiddle here

Related

Returning grouped junction table results in a JSON-formatted string

Say I have a table of people
+----+------+
| id | name |
+----+------+
| 1 | John |
| 2 | Mary |
| 3 | Jane |
+----+------+
And various tables for clothing of various types, e.g. a table of shoes
+----+----------+--------------------+---------+
| id | brand | name | type |
+----+----------+--------------------+---------+
| 1 | Converse | High tops | sneaker |
| 2 | Clarks | Tilden cap Oxfords | dress |
| 3 | Nike | Air Zoom | running |
+----+----------+--------------------+---------+
And then I have a junction table, where I’m storing all the clothing that each person has:
+--------+--------+-------+-------+
| person | shirts | pants | shoes |
+--------+--------+-------+-------+
| 1 | 3 | | |
| 1 | 4 | | |
| 1 | | 3 | |
| 1 | | | 5 |
| 2 | | 2 | |
| 2 | | | 2 |
| 2 | 3 | | |
...
I need a query that compiles this junction table into a return like so:
+----+------+--------------------+
| id | name | clothing items |
+----+------+--------------------+
| 1 | John | [JSON in a string] |
| 2 | Mary | [JSON in a string] |
| 3 | Jane | [JSON in a string] |
+----+------+--------------------+
Where the [JSON in a string] for each row should look like this:
{
"shirts":[3,4],
"pants":[3],
"shoes":[5]
}
How do I go about constructing this query in SQLITE?
Use SQLite's JSON Functions to aggregate in the junction table and do a LEFT join of people to that resultset:
WITH cte AS (
SELECT person,
json_object(
'shirts', json('[' || GROUP_CONCAT(shirts) || ']'),
'pants', json('[' || GROUP_CONCAT(pants) || ']'),
'shoes', json('[' || GROUP_CONCAT(shoes) || ']')
) clothing_items
FROM junction
GROUP BY person
)
SELECT p.id, p.name, c.clothing_items
FROM people p LEFT JOIN cte c
ON c.person = p.id;
I use GROUP_CONCAT() instead of json_group_array() to remove nulls.
See the demo.

how to insert into sql table additional values from second table if primary ids both start with 1?

the supply table:
supply_id | title | author | price | amount |
+-----------+----------------+------------------+--------+--------+
| 1 | Лирика | Пастернак Б.Л. | 518.99 | 2 |
| 2 | Черный человек | Есенин С.А. | 570.20 | 6 |
| 3 | Белая гвардия | Булгаков М.А. | 540.50 | 7 |
| 4 | Идиот | Достоевский Ф.М. | 360.80 | 3
I am trying to insert into book , all the values from supply:
+---------+-----------------------+------------------+--------+--------+
| book_id | title | author | price | amount |
+---------+-----------------------+------------------+--------+--------+
| 1 | Мастер и Маргарита | Булгаков М.А. | 670.99 | 3 |
| 2 | Белая гвардия | Булгаков М.А. | 540.50 | 5 |
| 3 | Идиот | Достоевский Ф.М. | 460.00 | 10 |
| 4 | Братья Карамазовы | Достоевский Ф.М. | 799.01 | 2 |
| 5 | Стихотворения и поэмы | Есенин С.А. | 650.00 | 15 |
+---------+-----------------------+------------------+--------+--------+
insert into book(title,author,price,amount)
select * from supply;
the primary ids conflict with each other - both tables start with id '1'
ERROR 1136: Column count doesn't match value count at row 1
Your columns in insert and select mismatches. You need to give explicit names of the columns instead of * in SELECT clause.
Try this:
insert into book(title,author,price,amount)
select title,author,price,amount from supply;

Comma separated column values in a laravel join query

I have a laravel webapp in which my database is structured via the below tables. The relationship is that transactions hasmany loading_hire_agreements via lhas_transactions.
Table name: transactions
| id | date | number |
|----|-------------------|--------|
| 1 | 12:00:00 00:00:00 | TRN#1 |
| 2 | 10:00:00 00:00:00 | TRN#2 |
Table name: loading_hire_agreements
| id | number |
|----|--------|
| 1 | JY1001 |
| 2 | JY1002 |
| 3 | JY1003 |
Table name: lhas_transactions
| transction_id | lha_id |
|---------------|--------|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
I want to write a join statement using laravel's DB:table()->join(\* any relevant join *\) so that the below output is generated.
| transactions.id | transactions.number | loading_hire_agreements.number |
|------------------|---------------------|--------------------------------|
| 1 | TRN#1 | JY1001, JY1002 |
| 2 | TRN#2 | JY1003 |
I "have" to use DB:table() because I am connecting this query to a front-end datatable service.
Try using GROUP_CONCAT and join all three tables:
$users = DB::table('transactions')
->join('lhas_transactions as lt', 'transactions.id', '=', 'lt.transaction_id')
->join('loading_hire_agreements as lha', 'lt.lha_id', '=', 'lha.id')
->select(DB::raw('transactions.id, transactions.number, GROUP_CONCAT(lha.number ORDER BY lha.id) AS agreements'))
->groupBy('transactions.id', 'transactions.number')
->get();

SQL compare multiple rows or partitions to find matches

The database I'm working on is DB2 and I have a problem similar to the following scenario:
Table Structure
-------------------------------
| Teacher Seating Arrangement |
-------------------------------
| PK | seat_argmt_id |
| | teacher_id |
-------------------------------
-----------------------------
| Seating Arrangement |
-----------------------------
|PK FK | seat_argmt_id |
|PK | Row_num |
|PK | seat_num |
|PK | child_name |
-----------------------------
Table Data
------------------------------
| Teacher Seating Arrangement|
------------------------------
| seat_argmt_id | teacher_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
------------------------------
---------------------------------------------------
| Seating Arrangement |
---------------------------------------------------
| seat_argmt_id | row_num | seat_num | child_name |
| 1 | 1 | 1 | Abe |
| 1 | 1 | 2 | Bob |
| 1 | 1 | 3 | Cat |
| | | | |
| 2 | 1 | 1 | Abe |
| 2 | 1 | 2 | Bob |
| 2 | 1 | 3 | Cat |
| | | | |
| 3 | 1 | 1 | Abe |
| 3 | 1 | 2 | Cat |
| 3 | 1 | 3 | Bob |
| | | | |
| 4 | 1 | 1 | Abe |
| 4 | 1 | 2 | Bob |
| 4 | 1 | 3 | Cat |
| 4 | 2 | 2 | Dan |
---------------------------------------------------
I want to see where there are duplicate seating arrangements for a teacher. And by duplicates I mean where the row_num, seat_num, and child_name are the same among different seat_argmt_id for one teacher_id. So with the data provided above, only seat id 1 and 2 are what I would want to pull back, as they are duplicates on everything but the seat id. If all the children on the 2nd table are exact (sans the primary & foreign key, which is seat_argmt_id in this case), I want to see that.
My initial thought was to do a count(*) group by row#, seat#, and child. Everything with a count of > 1 would mean it's a dupe and = 1 would mean it's unique. That logic only works if you are comparing single rows though. I need to compare multiple rows. I cannot figure out a way to do it via SQL. The solution I have involves going outside of SQL and works (probably). I'm just wondering if there is a way to do it in DB2.
Does this do what you want?
select d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
from seatingarrangement sa join
data d
on sa.seat_argmt_id = d.seat_argmt_id
group by d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
having count(*) > 1;
EDIT:
If you want to find two arrangements that are the same:
select sa1.seat_argmt_id, sa2.seat_argmt_id
from seatingarrangement sa1 join
seatingarrangement sa2
on sa1.seat_argmt_id < sa2.seat_argmt_id and
sa1.row_num = sa2.row_num and
sa1.seat_num = sa2.seat_num and
sa1.child_name = sa2.child_name
group by sa1.seat_argmt_id, sa2.seat_argmt_id
having count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa1.seat_argmt_id) and
count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa2.seat_argmt_id);
This finds the matches between two arrangements and then verifies that the counts are correct.

PostgreSQL JOIN with array type with array elements

I have two tables tags and users
Table Name: tags
| id | name |
| 1 | one |
| 2 | two |
| 3 | three |
| 4 | four |
| 5 | five |
Table Name: users
| id | fname | tags |
| 1 | Ram | {1,5} |
| 2 | Sham | {1,2,3,4} |
| 3 | Bham | {1,3} |
| 4 | Kam | {5,2} |
| 5 | Lam | {4,2} |
Expected output:
| id | fname | tags |
| 1 | Ram | one, five |
| 2 | Sham | one, two, three, four |
| 3 | Bham | one, three |
| 4 | Kam | five, two |
| 5 | Lam | four, two |
Trial-1 : using JOIN
SELECT I.id, I.fname, I.tags, J.name FROM users I
JOIN tags J ON J.id = ANY(I.cached_tag_ids)
LIMIT 1
Result:
| id | fname | tags |
| 1 | Ram | one |
| 1 | Ram | five |
Expected:
| id | fname | tags |
| 1 | Ram | one, five |
Your tags should have a INTEGER[] type.
CREATE TABLE users(
id SERIAL,
fname VARCHAR(50),
tags INTEGER[]
);
Then,
SELECT I.id, I.fname, array_agg(J.name)
FROM users I
LEFT JOIN tags J
ON J.id = ANY(I.tags)
GROUP BY fname,I.id ORDER BY id
should work. See sqlfiddle
This question may help.