Big_query SQL To have single row result instead of two rows - sql

Im using Big Query Sql here
This is the table build
This table is showing customer id_123 has purchase in type_shop and delivery_shop & also delivery_home .
Is it possible for me to get the result to be reflect in a single row instead of 2 different rows ?
I only want to show this customer id_123 purchased in type_shop & uses delivery_home & delivery_shop in a row
I tried a few methods using array_agg(stru) but it is still shows 2 rows of result instead of 1.
Not sure what other SQL function should i try here ? try searching for similar content in stack overflow but there isnt one that i can apply .

Assuming your sample data is your 1st table. Consider approach below:
with sample_data as (
select 'id_123' as customer, 'm' as gender, [1,1] as type_shop, [0,0] as type_online, [0,0] as delivery_pickup,[0,1] as delivery_home, [1,0] as delivery_shop,
union all select 'id_456' as customer, 'f' as gender, [1,0,1] as type_shop, [0,1,0] as type_online, [0,0,0] as delivery_pickup,[1,0,0] as delivery_home, [0,1,1] as delivery_shop,
),
normalize_data as (
select
customer,
gender,
type_shop[safe_offset(index)] as type_shop,
type_online[safe_offset(index)] as type_online,
delivery_pickup[safe_offset(index)] as delivery_pickup,
delivery_home[safe_offset(index)] as delivery_home,
delivery_shop[safe_offset(index)] as delivery_shop,
from sample_data,
unnest(generate_array(0,array_length(type_shop)-1)) as index
),
join_data as (
select
customer,
gender,
max(type_shop) as t_shop,
max(type_online) as t_online,
max(delivery_pickup) as delivery_pickup,
max(delivery_home) as delivery_home,
max(delivery_shop) as delivery_shop,
from normalize_data
group by customer,gender,type_shop,type_online
)
select
customer,
gender,
array_agg(t_shop) as type_shop,
array_agg(t_online) as type_online,
array_agg(delivery_pickup) as delivery_pickup,
array_agg(delivery_home) as delivery_home,
array_agg(delivery_shop) as delivery_shop,
from join_data
group by customer,gender
Output:

Related

Count Distinct values in one column based on other column

I am trying to count distinct values on Z_l based on value by using with clause. Sample data exercise included below.
please look at the picture, the distinct values of Z_l based on X='ny'
with distincz_l as (select ny.X, ny.z_l o.cnt From HOPL ny join (select X, count(*) as cnt from HOPL group by X) o on (ny.X = o.Z_l)) select * from HOPL;
You don't even need a WITH clause, since you just need one single sentence:
SELECT z_l, count(1)
FROM hopl
WHERE x='ny'
GROUP BY z_l
;

How to group this query with repeated values on one column and the other column containing no repeated data?

I'm practicing SQL and I'm trying to do a query of the best seller by section in a shop, I have this query so far:
select idrama, nombre, apellido, max(valortotal)
from(
select personarama.idrama, persona.nombre, persona.apellido,sum(detalle.cantidad*precio.valor) as valortotal
from persona,factura,precio,detalle, personarama
where persona.idpersona=factura.idvendedor
and personarama.idpersona=persona.idpersona
and factura.numfactura=detalle.numfactura
and precio.referencia=detalle.referencia
and factura.fecha between precio.fechaini and precio.fechafin
group by persona.idpersona, personarama.idrama, persona.nombre, persona.apellido
order by 4 DESC
) as vendedorRama
group by idrama, nombre, apellido;
when I execute the query I get the sellers by section and the amount of money they have sold.
And I need to get the best seller by section, and the only way to do this, is grouping by only idrama, but with this solution I cannot get the name and firstname of the seller. How could I group this data without losing those columns?
You can use window function like this:
select DISTINCT idrama, nombre, apellido, max(valortotal) OVER (PARTITION BY idrama) AS 'Max_of_valortotal'
from(
select personarama.idrama, persona.nombre, persona.apellido,sum(detalle.cantidad*precio.valor) as valortotal
from persona,factura,precio,detalle, personarama
where persona.idpersona=factura.idvendedor
and personarama.idpersona=persona.idpersona
and factura.numfactura=detalle.numfactura
and precio.referencia=detalle.referencia
and factura.fecha between precio.fechaini and precio.fechafin
group by persona.idpersona, personarama.idrama, persona.nombre, persona.apellido
order by 4 DESC
) as vendedorRama

SQL Nested Select -Subquery returned more than 1 value-

I have a table Sales with columns SalesID, SalesName, SalesCity, SalesState.
I am trying to come up with a query that only shows salesName where there is one SalesName per SalesCity. So for example, if SaleA is in Houston and SaleB is in Houston, SaleA and SaleB will not be returned.
select
SalesName, SalesCity, SalesState
from
Sales
where
(select count(*) from Sales group by SalesCity) = 1;
I am not entirely sure how to link the inner select back out. I need another column in the nested select to identify the SalesID. I am currently stuck and have made no progress.
You can get the names of cities that have only 1 sale by using GROUP BY and HAVING operators. Then use these results in your where clause:
SELECT SalesName, SalesCity, SalesState
FROM Sales WHERE SalesCity IN
(
SELECT SalesCity
FROM Sales
GROUP BY SalesCity
HAVING COUNT(SalesCity) = 1
)
You can do this without a subquery:
select MIN(SalesName) as SalesName, SalesCity, MIN(SalesState) as SalesState
from Sales
group by SalesCity
having count(*) = 1;
If there is only one row for the city, then the min() will return the value on that row.

SQL: Using UNION

Here is the question and database info.
Use the UNION command to prepare a full statement for customer 'C001' - it should be laid out as follows. (Note that the values shown below are not correct.) You may be able to use '' or NULL for blank values - if necessary use 0.
Here is a link to the webpage with the database info. http://sqlzoo.net/5_0.htm or see the image below.
Here is what I have tried:
SELECT sdate AS LineDate, "delivery" AS LEGEND, price*quantity AS Total,"" AS Amount
FROM shipped
JOIN product ON (shipped.product=product.id)
WHERE badguy='C001'
UNION
SELECT rdate,notes, "",receipt.amount
FROM receipt
WHERE badguy='C001'
Here is what I get back:
Wrong Answer. The correct answer has 5 row(s).
The amounts don't seem right in the amount column and I can't figure out how to order the data by the date since it is using two different date columns (sdate and rdate which are UNIONED).
Looks like the data in the example is being aggregated by date and charge type using group by, that's why you are getting too many rows.
Also, you can sort by the alias of the column (LineDate) and the order by clause will apply to all the rows in the union.
SELECT sdate AS LineDate, "delivery" AS LEGEND, SUM(price*quantity) AS Total,"" AS Amount
FROM shipped
JOIN product ON (shipped.product=product.id)
WHERE badguy='C001'
GROUP BY sdate
UNION
SELECT rdate, notes, "",receipt.amount
FROM receipt
WHERE badguy='C001'
ORDER BY LineDate
It's usually easiest to develop each part of the union separately. Pay attention to the use of "null" to separate the monetary columns. The first select gets to name the columns.
select s.sdate as tr_date, 'Delivery' as type, sum((s.quantity * p.price)) as extended_price, null as amount
from shipped s
inner join product p on p.id = s.product
where badguy = 'C001'
group by s.sdate
union all
select rdate, notes, null, sum(amount)
from receipt
where badguy = 'C001'
group by rdate, notes
order by tr_date

Selecting distinct values for multiple columns

I have a table where many pieces of data match to one in another column, similar to a tree, and then data at the 'leaf' about each specific leaf
eg
Food Group Name Caloric Value
Vegetables Broccoli 100
Vegetables Carrots 80
Fruits Apples 120
Fruits Bananas 120
Fruits Oranges 90
I would like to design a query that will return only the distinct values of each column, and then nulls to cover the overflow
eg
Food group Name Caloric Value
Vegetables Broccoli 100
Fruit Carrots 80
Apples 120
Bananas 90
Oranges
I'm not sure if this is possible, right now I've been trying to do it with cases, however I was hoping there would be a simpler way
Seems like you are simply trying to have all the distinct values at hand. Why? For displaying purposes? It's the application's job, not the server's. You could simply have three queries like this:
SELECT DISTINCT [Food Group] FROM atable;
SELECT DISTINCT Name FROM atable;
SELECT DISTINCT [Caloric Value] FROM atable;
and display their results accordingly.
But if you insist on having them all in one table, you might try this:
WITH atable ([Food Group], Name, [Caloric Value]) AS (
SELECT 'Vegetables', 'Broccoli', 100 UNION ALL
SELECT 'Vegetables', 'Carrots', 80 UNION ALL
SELECT 'Fruits', 'Apples', 120 UNION ALL
SELECT 'Fruits', 'Bananas', 120 UNION ALL
SELECT 'Fruits', 'Oranges', 90
),
atable_numbered AS (
SELECT
[Food Group], Name, [Caloric Value],
fg_rank = DENSE_RANK() OVER (ORDER BY [Food Group]),
n_rank = DENSE_RANK() OVER (ORDER BY Name),
cv_rank = DENSE_RANK() OVER (ORDER BY [Caloric Value])
FROM atable
)
SELECT
fg.[Food Group],
n.Name,
cv.[Caloric Value]
FROM (
SELECT fg_rank FROM atable_numbered UNION
SELECT n_rank FROM atable_numbered UNION
SELECT cv_rank FROM atable_numbered
) r (rank)
LEFT JOIN (
SELECT DISTINCT [Food Group], fg_rank
FROM atable_numbered) fg ON r.rank = fg.fg_rank
LEFT JOIN (
SELECT DISTINCT Name, n_rank
FROM atable_numbered) n ON r.rank = n.n_rank
LEFT JOIN (
SELECT DISTINCT [Caloric Value], cv_rank
FROM atable_numbered) cv ON r.rank = cv.cv_rank
ORDER BY r.rank
I guess what I would want to know is why you need this in one result set? What does the code look like that would consume this result? The attributes on each row have nothing to do with each other. If you want to, say, build the contents of a set of drop-down boxes, you're better off doing these one at a time. In your requested result set, you'd need to iterate through the dataset three times to do anything useful, and you would need to either check for NULL each time or needlessly iterate all the way to the end of the dataset.
If this is in a stored procedure, couldn't you run three separate SELECT DISTINCT and return the values as three results. Then you can consume them one at a time, which is what you would be doing anyway I would guess.
If there REALLY IS a connection between the values, you could add each of the results to an array or list, then access all three lists in parallel using the index.
Something like this maybe?
select *
from (
select case
when row_number() over (partition by fruit_group) = 1 then fruit_group
else null
end as fruit_group,
case
when row_number() over (partition by name) = 1 then name
else null
end as name,
case
when row_number() over (partition by caloric) = 1 then caloric
else null
end as caloric
from your_table
) t
where fruit_group is not null
or name is not null
or caloric is not null
But I fail to see any sense in this