How to Group by with sum of multi row in another table - sql

I have two tables. Ticket and TicketBasket as following image.
I want to select somthings like this:
|tId|tDate|customerName|expDate|tax|total|
|1 |xxx |xxx      |xxx  |2 |25 |
|2 |xxx |xxx      |xxx  |2 |20 |
|3 |xxx |xxx      |xxx  |2 |15 |
because of my sql command, the result has iteration with tId because each Ticket can have multi Items in it's basket.
my sql code is :
SELECT distinct Ticket.tId,dbo.ToCustomeDate(Ticket.tDate) 'tDate',
Ticket.customerName,dbo.ToCustomeDate(isnull(Ticket.expDate,Ticket.tDate)) 'expDate',
Ticket.tax,
(
(((TicketBasket.gamePrice*TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))+
(((TicketBasket.gamePrice * TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))* (Ticket.tax)/100))
) AS total
FROM Ticket right JOIN TicketBasket
ON Ticket.tId = TicketBasket.tId
but the result is
|tId|tDate|customerName|expDate|tax|total|
|1 |xxx |xxx      |xxx  |2 |10 |
|1 |xxx |xxx      |xxx  |2 |10 |
|1 |xxx |xxx      |xxx  |2 |5  |
|2 |xxx |xxx      |xxx  |2 |10 |
|2 |xxx |xxx      |xxx  |2 |10 |
|3 |xxx |xxx      |xxx  |2 |10 |
|3 |xxx |xxx      |xxx  |2 |5  |
I can handle this with cursor but I know it's a heavy load to execute the select query, so thanks for other solutions.

This should work..based on your query's output:
Select
iq.tld, iq.tDate, iq.customerName, iq.expDate,iq.tax,
SUM(total) as newTotal
from
(
SELECT distinct Ticket.tId,dbo.ToCustomeDate(Ticket.tDate) 'tDate',
Ticket.customerName,dbo.ToCustomeDate(isnull(Ticket.expDate,Ticket.tDate)) 'expDate',
Ticket.tax,
(
(((TicketBasket.gamePrice*TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))+
(((TicketBasket.gamePrice * TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))* (Ticket.tax)/100))
) AS total
FROM Ticket right JOIN TicketBasket
ON Ticket.tId = TicketBasket.tId
)iq
group by iq.tld, iq.tDate, iq.customerName, iq.expDate,iq.tax

Related

Create recursive CTE for this table [duplicate]

This question already has answers here:
The maximum recursion 100 has been exhausted before statement completion
(2 answers)
Closed 3 months ago.
I have a table like this:
|id |name |parent|
+-------+----------+------+
|1 |iran | |
|2 |iraq | |
|3 |tehran |1 |
|4 |tehran |3 |
|5 |Vaiasr St |4 |
|6 |Fars |1 |
|7 |shiraz |6 |
It's about addresses from country to street. I want to create address by recursive cte like this:
with cte_address as
(
select
ID, [Name], parent
from
[Address]
where
Parent is null
union all
select
a.ID, a.[name], a.Parent
from
address a
inner join
cte_address c on a.parent = c.id
)
select *
from cte_address
But I get an error:
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
you have to use option (maxrecursion 0) at the end of your select query,Maxrecursion 0 allows infinite recursion:
with cte_address as
(
...
...
)
select * from cte_address
option (maxrecursion 0)
Note :
Limiting the number of recursions allowed for a specific query in SQL Server with the 100 default value prevents the cause of an infinite loop situation due to a poorly designed recursive CTE query.

Looking for Postgres query which can provide output like MongoDB group by function

Product table
|_id|name |
|---|------|
|3 |Laptop|
Size table
|_id|product_id|size|
|---|----------|----|
|5 |3 |15 |
|6 |3 |17 |
Query:
select tp._id, tp.name, ts.size from test_product tp
left join test_size ts on tp._id = ts.product_id
group by tp._id, tp.name, ts.size
where tp._id = 3 limit 10 offset 0
Current output:
|_id|name |size|
|---|------|----|
|3 |Laptop|15 |
|3 |Laptop|17 |
Expected output
|_id|name |size |
|---|------|-------|
|3 |Laptop|[15,17]|
Note:
Due to current query I'm getting 2 record for the same product and my limit and offset query logic is getting false and not getting proper count. I'm not well aware of Postgres queries for this kind of situation. So I need solution for this so my limit and offset logic will be correct for fetching data and for this query my count of product will be 1.
Use array_agg():
SELECT
tp._id,
tp.name,
ARRAY_AGG(ts.size ORDER BY ts.size) -- ORDER BY to get consistent results
FROM
test_product tp
LEFT JOIN test_size ts ON tp._id = ts.product_id
GROUP BY
tp._id,
tp.name
WHERE
tp._id = 3
LIMIT 10
OFFSET 0;
The ORDER BY within the aggregation is optional, but it's always nice to get consistent results over and over again.

SQL if one of GROUPed rows includes A choose it else choose what's left

The table looks like that:
|ID |status |Date |
|1 |declined|01.01.2010|
|1 |declined|04.01.2010|
|1 |approved|06.01.2010|
|2 |approved|05.02.2010|
|3 |NULL |05.02.2010|
So I wanna group them by ID but at the same time I want to get the latest status for each group
I tried using "Having" but it didn't work for me (I don't know how to use group on everything except status, and to choose only the latest value of status) for each group
Expected result:
|ID |status |Date |
|1 |approved|06.01.2010|
|2 |approved|05.02.2010|
|3 |NULL |05.02.2010|
You can use a correlated subquery:
select t.*
from yourtable t
where t.date = (select max(t2.date) from yourtable t2 where t2.id = t.id);
IS ID a group within the context of your question?
The query down here shows the id's that have approved or null as status.
Id's (or groups) are displayed in ascending order.
SELECT * FROM `t1`
WHERE status ='approved' OR status ='NULL'
ORDER BY date ASC
example: https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=b61dc18af6193dc30cd9692679e6513e

sum values of a column over distinct values of other column

I'm using sybase database, an extraction of my table would be :
ClientNumber|ArticlesBought|TotalAllowed
2223 |2 |20
2223 |1 |20
2226 |3 |25
2226 |2 |25
2227 |1 |20
What I need is to calculate the sum of all the articlesBought and devide it by the sum of the total allowed for the distinct clients.
sum(ArticlesBought) = 9 and sum(TotalAllowed)= 65 and not 110
My first question is can I do this in one query?
I tried using:
select sum(TotalAllowed)
from myTable
group by ClientNumber
but it returns 40, 45 and 20, which is wrong.
Could you please help me ?
Thank you
Select my.ClientNumber , my2.TotalArticlesBought/ClientData.TotalAllowedperClient from tblFabSource my
inner join (select sum(Distinct TotalAllowed)as TotalAllowedperClient,ClientNumber from myTable group by ClientNumber)
ClientData on ClientData .ClientNumber=my.ClientNumber
inner join (select sum(ArticlesBought)as TotalArticlesBought from myTable ) my2
on my2.TotalArticlesBought>0
select SUM(articlesbought), SUM(DISTINCT totalallowed) from tablename
you can use a distinct on the sum of totalallowed

SQL query to return a grouped result as a single row

If I have a jobs table like:
|id|created_at |status |
----------------------------
|1 |01-01-2015 |error |
|2 |01-01-2015 |complete |
|3 |01-01-2015 |error |
|4 |01-02-2015 |complete |
|5 |01-02-2015 |complete |
|6 |01-03-2015 |error |
|7 |01-03-2015 |on hold |
|8 |01-03-2015 |complete |
I want a query that will group them by date and count the occurrence of each status and the total status for that date.
SELECT created_at status, count(status), created_at
FROM jobs
GROUP BY created_at, status;
Which gives me
|created_at |status |count|
-------------------------------
|01-01-2015 |error |2
|01-01-2015 |complete |1
|01-02-2015 |complete |2
|01-03-2015 |error |1
|01-03-2015 |on hold |1
|01-03-2015 |complete |1
I would like to now condense this down to a single row per created_at unique date with some sort of multi column layout for each status. One constraint is that status is any one of 5 possible words but each date might not have one of every status. Also I would like a total of all statuses for each day. So desired results would look like:
|date |total |errors|completed|on_hold|
----------------------------------------------
|01-01-2015 |3 |2 |1 |null
|01-02-2015 |2 |null |2 |null
|01-03-2015 |3 |1 |1 |1
the columns could be built dynamically from something like
SELECT DISTINCT status FROM jobs;
with a null result for any day that doesn't contain any of that type of status. I am no SQL expert but am trying to do this in a DB view so that I don't have to bog down doing multiple queries in Rails.
I am using Postresql but would like to try to keep it straight SQL. I have tried to understand aggregate function enough to use some other tools but not succeeding.
The following should work in any RDBMS:
SELECT created_at, count(status) AS total,
sum(case when status = 'error' then 1 end) as errors,
sum(case when status = 'complete' then 1 end) as completed,
sum(case when status = 'on hold' then 1 end) as on_hold
FROM jobs
GROUP BY created_at;
The query uses conditional aggregation so as to pivot grouped data. It assumes that status values are known before-hand. If you have additional cases of status values, just add the corresponding sum(case ... expression.
Demo here
An actual crosstab query would look like this:
SELECT * FROM crosstab(
$$SELECT created_at, status, count(*) AS ct
FROM jobs
GROUP BY 1, 2
ORDER BY 1, 2$$
,$$SELECT unnest('{error,complete,"on hold"}'::text[])$$)
AS ct (date date, errors int, completed int, on_hold int);
Should perform very well.
Basics:
PostgreSQL Crosstab Query
The above does not yet include the total per date.
Postgres 9.5 introduces the ROLLUP clause, which is perfect for the case:
SELECT * FROM crosstab(
$$SELECT created_at, COALESCE(status, 'total'), ct
FROM (
SELECT created_at, status, count(*) AS ct
FROM jobs
GROUP BY created_at, ROLLUP(status)
) sub
ORDER BY 1, 2$$
,$$SELECT unnest('{total,error,complete,"on hold"}'::text[])$$)
AS ct (date date, total int, errors int, completed int, on_hold int);
Up to Postgres 9.4, use this query instead:
WITH cte AS (
SELECT created_at, status, count(*) AS ct
FROM jobs
GROUP BY 1, 2
)
TABLE cte
UNION ALL
SELECT created_at, 'total', sum(ct)
FROM cte
GROUP BY 1
ORDER BY 1
Related:
Grouping() equivalent in PostgreSQL?
If you want to stick to a simple query, this is a bit shorter:
SELECT created_at
, count(*) AS total
, count(status = 'error' OR NULL) AS errors
, count(status = 'complete' OR NULL) AS completed
, count(status = 'on hold' OR NULL) AS on_hold
FROM jobs
GROUP BY 1;
count(status) for the total per date is error-prone, because it would not count rows with NULL values in status. Use count(*) instead, which is also shorter and a bit faster.
Here is a list of techniques:
For absolute performance, is SUM faster or COUNT?
In Postgres 9.4+ use the new aggregate FILTER clause, like #a_horse mentioned:
SELECT created_at
, count(*) AS total
, count(*) FILTER (WHERE status = 'error') AS errors
, count(*) FILTER (WHERE status = 'complete') AS completed
, count(*) FILTER (WHERE status = 'on hold') AS on_hold
FROM jobs
GROUP BY 1;
Details:
How can I simplify this game statistics query?