Complex join with different data types in laravel 8 query builder - sql

I have two tables Infos and nationalities like this :
1) Table infos:
id(pk)
name
nationality(varchar) accept multiple nationalities
1
John
1,2,3
2
Camilia
2,4
1) Table nationalities :
id(pk) int
desig
1
German
2
Turkey
3
Algeria
4
Qatar
I want to get in the output something like this:
desig
count
German
1
Turkey
2
Algeria
1
Qatar
1
So for that I tried something like this(but it did not help):
$total_nationalite = DB::table('infos')
->select('desig', 'nationalities.id',DB::raw('COUNT(*) as count'))
->join('nationalities', DB::raw('CAST(nationalities.id as varchar)'), '=', 'infos.nationality')
->groupBy('desig','nationalities.id')
->get();
please any suggestion with the query builder or with a simple query in postgres.

Having a comma separated list of values text in nationality field is a bad idea. Have an array of integers or - much better - a normalized design. Anyway here is a native query solution.
select desig, count(*) count
from
(
select unnest(string_to_array(nationality, ','))::integer nat_id
from infos
) t
join nationalities n on t.nat_id = n.id
group by desig;
DB Fiddle
desig
count
German
1
Qatar
1
Algeria
1
Turkey
2

Related

How to assign data without repetition in SQL

I need to create automatic weekly assignments of items to sites for my employees.
The items table items_bank looks like that(of course there will be a lot of items with few more languages) :
**item_id** **item_name** **language**
1 Jorge Garcia English
2 Chrissy Metz English
3 Nina Hagen German
4 Harald Glööckle German
5 Melissa Anderson French
6 Pauley Perrette French
My second table is the sites table :
**site_id** **site_name**
1 DR
2 LI
3 IG
I need to assign every week items to the sites with the following constraints :
For each site assign at least X items of English, Y items of German, and so on...
we want to create diversity - so we would like to avoid repeating the assignments of the 2 weeks before
I think we need to create another table in which we can save there the history of the last 2 weeks' assignments.
right now I managed to create an SQL query that assigns items but I don't know how to take the constraints under consideration this is what I create so far :
WITH numbered_tasks AS (
SELECT t.*, row_number() OVER (ORDER BY rand()) item_number, count(*) OVER () total_items
FROM item_bank t
),
numbered_employees AS (
SELECT e.*,row_number() OVER (ORDER BY rand()) site_number,
count(*) OVER () total_sites
FROM sites_bank e
)
SELECT nt.item_name,
ne.acronym
FROM numbered_tasks nt
INNER JOIN numbered_employees ne
ON ne.site_number-1 = mod(nt.item_number-1, ne.total_sites)
Expected results are for the example which says :
site_id=1 have to get 1 item with the English language
site_id=2 have to get 1 item with the German language
site_id=1 have to get 1 item with the French language
**item_id** **language** **Week_number** **site**
1 English 1 1
4 German 1 2
5 French 1 3
Any help will be appreciated!

SQL subquery as part of LIKE search

From this Question we learned to use a subquery to find information once-removed.
Subquery we learned :
SELECT * FROM papers WHERE writer_id IN ( SELECT id FROM writers WHERE boss_id = 4 );
Now, I need to search a table, both in column values that table, and in column values related by id on another table.
Here are the same tables, but col values contain more text for our "searching" reference...
writers :
id
name
boss_id
1
John Jonno
2
2
Bill Bosworth
2
3
Andy Seaside
4
4
Hank Little
4
5
Alex Crisp
4
The writers have papers they write...
papers :
id
title
writer_id
1
Boston
1
2
Chicago
4
3
Cisco
3
4
Seattle
2
5
North
5
I can use this to search only the names on writers...
Search only writers.name : (Not what I want to do)
SELECT * FROM writers WHERE LOWER(name) LIKE LOWER('%is%');
Output for above search : (Not what I want to do)
id
name
boss_id
5
Alex Crisp
4
I want to return cols from writers (not papers), but searching text both in writers.name and the writers.id-associated papers.title.
For example, if I searched "is", I would get both:
Alex Crisp (for 'is' in the name 'Crisp')
Andy Seaside (because Andy wrote a paper with 'is' in the title 'Cisco')
Output for "is" search :
id
title
writer_id
2
Chicago
4
4
Seattle
2
Here's what I have that doesn't work:
SELECT * FROM papers WHERE LOWER(title) LIKE LOWER('%is%') OR writer_id ( writers=writer_id WHERE LOWER(name) LIKE LOWER('%$is%') );
The best way to express this criteria is by using a correlated query with exists:
select *
from writers w
where Lower(w.name) like '%is%'
or exists (
select * from papers p
where p.writer_id = w.id and Lower(p.title) like '%is%'
);
Note you don't need to use lower on the string you are providing, and you should only use lower if your collation truly is case-sensitive as using the function makes the search predicate unsargable.
Since you want to return cols from writers (not papers) you should select them first, and use stuff from papers in the criteria
select *
from writers w
where
w.name like '%is%'
or
w.id in (select p.writer_id
paper p
where p.title like '%is%'
)
You can add your LOWER functions (my sql environment is not case-sensitive, so I didn't need them)

SQL row names and row flags

I have trouble understanding row flags. The below question can clear it for me:
Is it possible to store a name and its flag in the same cell in SQL?
Consider:
If you have a table known as cars with the columns number_plate, colour, and brand_name. The brand_name has a name and a flag.
How would one store that in a single column? If it is not possible or advised, explain why and how to do it.
How would you then get the number of cars from a given country (based on the unique number_plate(primary key)) and the country flag?
I think you are trying to design a schema but haven't quite got the hang of foreign keys.
In your example, you'd have the following tables:
country:
country_id name continent
-----------------------------------
1 Germany Europe
2 Japan Asia
3 USA N.America
Brand
Brand_id name country_id (foreign key)
---------------------------------------------
1 Mercedes 1
2 Toyota 2
3 BMW 1
4 Chrysler 3
Car
Number_plate colour brand_id
------------------------------------------
xxx-yy-zz Green 1
aa-bb-cc Red 1
kkk-l-mmm Orange 2
....
To find the number of cars, based on the country where the brand is based, you'd do something like:
select country.name,
count(*)
from car
inner join brand on car.brand_id = brand.brand_id
inner join country on brand.country_id = country.country_id
group by country.name
Let's say name and flag are two separate columns. Using concat function they can be stored into a single column named brand_name.
select number_plate, colour, concat(name,' ',flag) as brand_name from cars
To get the count of cars(unique) based on a flag
select * from
(select
distinct number_plate,
colour,
concat(name,' ',flag) as brand_name from cars
) a
where brand_name like '%UK%'
Demo

Table Join issue

Right now I've got a Main table in which I am uploading data. Because the Main table has many different duplicates, I Append various data out of the Main table into other tables such as, username, phone number, and locations in order to keep things optimized. Once I have everything stripped down from the Main table, I then append what's left into a final optimized Main table. Before this happens though, I run a select query joining all the stripped tables with the original Main table in order to connect the IDs from each table, with the correct data. For example:
Original Main Table
--Name---------Number------Due Date-------Location-------Charges Monthly-----Charges Total--
John Smith 111-1111 4/3 Chicago 234.56 500.23
Todd Jones 222-2222 4/3 New York 174.34 323.56
John Smith 111-1111 4/3 Chicago 274.56 670.23
Bill James 333-3333 4/3 Orlando 100.00 100.00
This gets split into 3 tables (name, number, location) and then there is a date table with all the dates for the year:
Name Table Number Table Location Table Due Date Table
--ID---Name------ -ID--Number--------- ---ID---Location---- --Date---
1 John Smith 1 111-1111 1 Chicago 4/1
2 Todd Jones 2 222-2222 2 New York 4/2
3 Bill James 3 333-3333 3 Orlando 4/3
Before The Original table gets stripped, I run a select query that grabs the ID from the 3 new tables, and joins them based on the connection they have with the original Main table.
Select Output
--Name ID----Number ID---Location ID---Due Date--
1 1 1 4/3
2 2 2 4/3
1 1 1 4/3
3 3 3 4/3
My issue comes when I need to introduce a new table that isn't able to be tied into the Original Main Table. I have an inventory table that, much like the original Main table, has duplicates and needs to be optimized. I do this by creating a secondary table that takes all the duplicated devices out and put them in their own table, and then strips the username and number out and puts them into their tables. I would like to add the IDs from this new device table into the select output that I have above. Resulting in:
Select Output
--Name ID----Number ID---Location ID---Due Date--Device ID---
1 1 1 4/3 1
2 2 2 4/3 1
1 1 1 4/3 2
3 3 3 4/3 1
Unlike the previous tables, the device table has no relationship to the originalMain Table, which is what is causing me so much headache. I can't seem to find a way to make this happen...is there anyway to accomplish this?
Any two tables can be joined. A table represents an application relationship. In some versions (not the original) of Entity-Relationship Modelling (notice that the "R" in E-R stands for "(application) relationship"!) a foreign key is sometimes called a "relationship". You do not need other tables or FKs to join any two tables.
Explain, in terms of its column names and the values for those names, exactly when a row should turn up in the result. Maybe you want:
SELECT *
FROM the stripped-and-ID'd version of the Original AS o
JOIN the stripped-and-ID'd version of the Device AS d
USING NameID, NumberID, LocationID and DueDate
Ie
SELECT *
FROM the stripped-and-ID'd version of the Original AS o
JOIN the stripped-and-ID'd version of the Device AS d
ON o.NameID=d.NameId AND o.NumberID=d.NumberID
AND o.LocationID=d.LocationID AND o.DueDateID=d.DueDate.
Suppose p(a,...) is some statement parameterized by a,... .
If o holds the rows where o(NameID,NumberID,LocationID,DueDate) and d holds the rows where d(NameID,NumberID,LocationID,DueDate,DeviceID) then the above holds the rows where o(NameID, NumberID, LocationID, DueDate) AND d(NameID,NumberID,LocationID,DueDate,DeviceID). But you really have not explained what rows you want.
The only way to "join" tables that have no relation is by unioning them together:
select attribute1, attribute2, ... , attributeN
from table1
where <predicate>
union // or union all
select attribute1, attribute2, ... , attributeN
from table2
where <predicate>
the where clauses are obviously optional
EDIT
optionally you could join the tables together by stating ON true which will act like a cross product

statistic syntax in access

I want to do some statistic for the Point in my appliation,this is the columns for Point table:
id type city
1 food NewYork
2 food Washington
3 sport NewYork
4 food .....
Each point belongs to a certain type and located at the certain city.
Now I want to caculate the numbers of points in different city for each type.
For example, there are two types here :food and sport.
Then I want to know:
how many points of `food` and `sport` at NewYork
how many points of `food` and `sport` at Washington
how many points of `food` and `sport` at Chicago
......
I have tried this:
select type,count(*) as num from point group by type ;
But I can not group the by the city.
How to make it?
Update
id type city
1 food NewYork
2 sport NewYork
3 food Chicago
4 food San
And I want to get something like this:
NewYork Chicago San
food 2 1 1
sport 1 0 0
I will use the html table and chart to display these datas.
So I need to do the counting, I can use something like this:
select count(*) from point where type='food' and city ='San'
select count(*) from point where type='food' and city ='NewYork'
....
However I think this is a bad idea,so I wonder if I can use the sql to do the counting.
BTW,for these table data,how do people organization their structure using json?
this's what you want:
SELECT city,
COUNT(CASE WHEN [type] = 'food' THEN 1 END) AS FoodCount,
COUNT(CASE WHEN [type] = 'sport' THEN 1 END) AS SportCount
FROM point
GROUP BY city
UPDATE:
To get the results in an aggregated row/column format you need to use a pivot table. In Access it's called a Crosstab query. You can use the Crosstab query wizard to generate the query via a nice UI or cut straight to the SQL:
TRANSFORM COUNT(id) AS CountOfId
SELECT type
FROM point
GROUP BY type
PIVOT city
The grouping is used to count the number of Id's for each type. The additional PIVOT clause groups the data by city and displays each grouping in a separate column. The end result looks something like this:
NewYork Chicago San
food 2 1 1
sport 1 0 0