I have got a table of orders placed by customer , what i want is to check from which part of the country orders are coming historically, I can only check this by postcodes , for intance an order with post code SK... means its stockport , similarly the post code starting from M .. means the order is from manchester, Is it possible to write a query which can count the orders by postcode.
Some of the fields of the Order table:
OrderNumber OGUID custID firstname last name address postcode email authorisation date etc...
Any suggestion or assistance will be appreciated.
Thanks
Here is way that works... but it can get too long for a huge list. I will try to find a way around that problem.
SELECT
CASE
WHEN postcode LIKE 'SK%' THEN 'SK'
WHEN postcode LIKE 'M%' THEN 'M'
END AS group_by_value
, COUNT(*) AS group_by_count
FROM [Table] a
GROUP BY
CASE
WHEN postcode LIKE 'SK%' THEN 'SK'
WHEN postcode LIKE 'M%' THEN 'M'
END
If you have a table that contains the city code and city name, then you might be able to use something like the following which joins your orders table to the codes using a LIKE:
select o.postcode,
c.city,
count(c.code) over(partition by c.code) Total
from orders o
inner join codes c
on o.postcode like '%'+c.code+'%'
See SQL Fiddle with Demo
You can use GROUP BY to get the total number of orders in each postcode:
select postcode, count(postcode) TotalOrdersByPostCode
from orders
group by postcode
If you want the City included, then you can also GROUP BY city:
select city, postcode, count(postcode) TotalOrdersByPostCode
from orders
group by city, postcode
select count(1) over(partition by postcode) as countByPostcode, othecolumnhere
from Order
Have you tried something like this? The town part of the postcode will be the first 1 or 2 bytes, delimited by a number after, I think. So this will give you the first few letters.
select substring(postcode,1, patindex('%[0-9]%',postcode)-1), count(*)
from Order
group by substring(postcode,1, patindex('%[0-9]%',postcode)-1)
Then you'll have to decode M into Manchester, W into West London, GU into Guildford etc...
Related
Thank you Stack-Community,
This is probably obvious for most of you but I just don't understand why it doesn't work.
I am using the Northwind database and lets say I am trying to find the countries that or not occurring twice but are listed either more than twice or less often.
I already figured out other ways of doing it with a having statement, so I am not looking for alternatives but trying to understand why my initial attempt is not working.
I look at it and look at it and it makes perfect sense to me. Can someone explain what's the problem?
SELECT country, count(country)
FROM Customers
WHERE 2 not in (SELECT count(country) FROM Customers GROUP BY country)
GROUP BY country
;
You need correlated subquery:
SELECT country, count(country)
FROM Customers c
WHERE 2 not in (SELECT count(country) FROM Customers c2
WHERE c2.country = c.country )
GROUP BY country;
Otherwise you get something like:
SELECT country, count(country)
FROM Customers c
WHERE 2 not in (1,2,3) -- false in every case and empty resultset
GROUP BY country;
Imagine that you have:
1, 'UK' -- 1
2, 'DE' -- 2
3, 'DE'
4, 'RU' -- 1
Now you will get equivalent of
SELECT country, count(country)
FROM Customers c
WHERE 2 not in (1,2,1) -- false in every case and empty resultset
GROUP BY country;
-- 0 rows selected
I am working on a SQL assignment in Oracle. There are two tables.
table1 is called Person10:
fields include: ID, Fname, Lname, State, DOH, JobTitle, Salary, Cat.
table2 is called StateInfo:
fields include: State, Statename, Capital, Nickname, Pop2010, pop2000, pop1990, sqmiles.
Question:
Create a view named A10T2 that will display the StateName, Capital and Nickname of the states that have at least 25 people in the Person10 table with a Cat value of N and an annual salary between $75,000 and $125,000. The three column headings should be StateName, Capital and Nickname. The rows should be sorted by the name of the state.
What I have :
CREATE VIEW A10T2 AS
SELECT StateName, Capital, Nickname
FROM STATEINFO INNER JOIN PERSON10 ON
STATEINFO.STATE = PERSON10.STATE
WHERE Person10.CAT = 'N' AND
Person10.Salary in BETWEEN (75000 AND 125000) AND
count(Person10.CAT) >= 25
ORDER BY STATE;
It gives me an error saying missing expression. I may need a group expression... but i dont know what I am doing wrong.
Yeah I originally messed this up when I first answered this because it was on the fly and I didn't have a chance to test what I was putting down. I forgot using a GROUP BY is more suited for aggregate functions (Like SUM, AVG and COUNT in the select) and that's probably why it's throwing the error. Using a ORDER BY is probably the correct option in this case. And you want to order your results by the state so you would use StateName.
SELECT S.StateName, S.Capital, S.Nickname
FROM STATEINFO S
INNER JOIN PERSON10 P ON S.STATE = P.STATE
WHERE P.CAT = 'N'
AND P.Salary BETWEEN 75000 AND 125000
ORDER BY S.StateName
HAVING count(P.CAT) >= 25;
Try moving your count() to HAVING instead of WHERE. You'll also need a GROUP BY clause containing StateName, Capital, and Nickname.
I know this link is Microsoft, not Oracle, but it should be helpful.
https://msdn.microsoft.com/en-us/library/ms180199.aspx?f=255&MSPPError=-2147217396
I'm no Oracle expert, but I'm pretty sure
Person10.Salary in BETWEEN (75000 AND 125000)
should be
Person10.Salary BETWEEN 75000 AND 125000
(no IN and no parentheses). That's how all other SQL dialects I know of work.
Also, move the COUNT() from the WHERE clause to a HAVING clause:
CREATE VIEW A10T2 AS
SELECT StateName, Capital, Nickname
FROM STATEINFO INNER JOIN PERSON10 ON
STATEINFO.STATE = PERSON10.STATE
WHERE Person10.CAT = 'N' AND
Person10.Salary BETWEEN 75000 AND 125000
ORDER BY STATE
HAVING count(Person10.CAT) >= 25;
You can try using a Sub Query like this.
CREATE VIEW A10T2 AS
SELECT statename, capital, nickname
FROM stateinfo
WHERE statename IN (SELECT statename
FROM person10
WHERE Cat = 'N'
AND Salary BETWEEN 75000 AND 125000
GROUP BY statename
HAVING COUNT(*) >= 25)
ORDER BY statename
I am using the MySQL world.sql database. Exactly what is in it doesn't matter, but the schema that matters to use looks like:
CREATE TABLE city (
name char(35),
country_code char(3),
population int(11),
);
CREATE TABLE country (
code char(3),
name char(52),
population int(11)
);
The query in question is, in english, "for each country, give me its name and population, along with the name and population for the city who has the highest ratio of its population to the country's population"
Currently I have the following SQL:
SELECT t.name, t.population, c.name, c.population
FROM country c
JOIN city t
ON t.country_code = c.code
WHERE t.population / c.population = (
SELECT MAX(tt.population / c.population)
FROM city tt
WHERE t.country_code = tt.country_code
)
Currently the query takes about 10 minutes to run on my SQLite database. The world.sql database isn't large (4000-5000 rows?) so I'm guessing I'm doing something wrong here.
I currently don't have any sort of indexes or anything: the database is an empty database with this dataset (https://dl.dropboxusercontent.com/u/7997532/world.sql) entered into it. Could anyone give me any pointers as to what I need to fix to make it run in a reasonable amount of time?
EDIT: well here's another twist to the question:
This runs in <2 seconds
SELECT t.name, t.population, c.name, c.population
FROM country c
JOIN city t
ON t.country_code = c.code
WHERE t.population * 1.0 / c.population = (
SELECT MAX(tt.population * 1.0 / c.population)
FROM city tt
WHERE tt.country_code = t.country_code
)
While this take 10 minutes to run
SELECT t.name, t.population, c.name, c.population
FROM country c
JOIN city t
ON t.country_code = c.code
AND t.population * 1.0 / c.population = (
SELECT MAX(tt.population * 1.0 / c.population)
FROM city tt
WHERE tt.country_code = t.country_code
)
Is the solution then to simply stuff as much as possible into the ON clause when i'm doing JOINs? It seems in this case I can get away without an index if I do that...
For each country, the city that has the highest ratio of population to it's country's population is the city with the highest population, so try this:
SELECT t.name, t.population, c.name, c.population
FROM country c
JOIN city t
ON t.country_code = c.code
And population =
(Select Max(population) from city
Where country_code = c.Code)
But this may still not improve performance much... if you have no indicies. You need to put an index on country.code, and on city.country_code
Ideally, I would first start with indexes and consider adding a computed field that pre-calculates the t.population / c.population into a link table
So for each country and city, you can look up it's ratio of population without computing in RBAR.
I suggest adding numeric primary keys to both tables and a foreign key on country_code in your city table. One of the benefits will be better performance because primary keys are indexed.
Edit starts here
Since the question doesn't ask you to provide the actual ratio, don't worry about trying to calculate it. The city with the highest population in the country will have the highest proportion of the country's population.
The database I'm accessing has two tables I need to query using DB2 SQL, shown here as nametable and addresstable. The query is for finding all of the people with a certain balance due. The addresses are stored in a separate table to keep track of address changes. In addresstable, the latest address is determined by a sequence number (ADDRSEQUENCE). The AddressID field is present in both tables, and is what ties each person to specific addresses. The highest sequence number is the current address. I need that current address for each person and only that one. I know I'm going to have to use MAX somewhere for the sequence number, but I can't figure out how to position it given the join. Here's my current query, which of course returns all addresses...
SELECT NAMETABLE.ACCTNUM AS ACCOUNTNUMBER,
NAMETABLE.NMELASTBUS AS LASTNAME,
NAMETABLE.NAME_FIRST AS FIRSTNAME,
NAMETABLE.BALDUE AS BALANCEDUE,
ADDRESSTABLE.STREETNAME AS ADDR,
ADDRESSTABLE.ADDRLINE2 AS
ADDRLINE2,ADDRESSTABLE.CITYPARISH AS CITY,
ADDRESSTABLE.ADDRSTATE AS STATE,
ADDRESSTABLE.ZIPCODE AS ZIP,
ADDRESSTABLE.ADDIDSEQNO AS ADDRSEQUENCE
FROM NAMETABLE JOIN ADDRESSTABLE ON NAMETABLE.ADDRESSID = ADDRESSTABLE.ADDRESSID
WHERE NAMETABLE.BALANCEDUE >= '50.00'
You can do a sub-select on the MAX(ADDRSEQUENCE) like so:
SELECT
N.ACCTNUM AS ACCOUNTNUMBER
,N.NMELASTBUS AS LASTNAME
,N.NAME_FIRST AS FIRSTNAME
,N.BALDUE AS BALANCEDUE
,A.STREETNAME AS ADDR,
,A.ADDRLINE2 AS
,A.ADDRLINE2
,A.CITYPARISH AS CITY,
,A.ADDRSTATE AS STATE,
,A.ZIPCODE AS ZIP,
FROM NAMETABLE AS N
JOIN ADDRESSTABLE AS A
ON N.ADDRESSID = A.ADDRESSID
WHERE N.BALANCEDUE >= '50.00'
AND A.ADDRSEQUENCE = (
SELECT MAX(ADDRSEQUENCE)
FROM ADDRESSTABLE AS A2
WHERE A.ADDRESSID = A2.ADDRESSID
)
This is pretty quick in DB2.
You can use a row_number and partition by to do this. Something like this:
with orderedaddress as (
select row_number() over (partition by ADDRESSID order by ADDRSEQUENCE desc) as rown,
STREETNAME,ADDRESSID, ... from ADDRESSTABLE
)
select NAMETABLE.ACCTNUM AS ACCOUNTNUMBER,
...
oa.STREETNAME
...
from NAMETABLE JOIN orderedaddress oa on NAMETABLE.ADDRESSID = oa.ADDRESSID
where oa.rown = 1
and NAMETABLE.BALANCEDUE >= '50.00'
In my Oracle database, I have a table called Customers. There are fields for name, zip, city etc., but there are also fields for invoicename, invoicezip and invoicecity. For some records the invoicespecific fields are not set, and in that case the invoice method should use the information from name, zip and city.
I use the following query now:
select
case when c.InvoiceName is null then c.Name else c.InvoiceName end as Name,
case when c.InvoiceName is null then c.Zip else c.InvoiceZip end as Zip,
case when c.InvoiceName is null then c.City else c.InvoiceCity end as City
<...>
from
Customers c
where
Accountnumber=:accountnumber
Note that the check is on InvoiceName only. If that value is set I want to return all the invoice-specific fields, if not I want to return all the "non-invoice-specific" fields.
What I would like to do is something like this
select
case when c.InvoiceName is not null
then
c.InvoiceName as Name,
c.InvoiceZip as Zip,
c.InvoiceCity as City
<...>
else
c.Name,
c.Zip,
c.City
<...>
end
from
customers c
Is there way to do this or something like it?
A CASE expression can only return one value, not 3, so you do need the 3 CASE expressions. Using DECODE would be slightly more concise, but DECODE is Oracle-specific whereas CASE is ANSI standard and so preferable. But for info it would look like:
select
decode(c.InvoiceName,null,c.Name,c.InvoiceName) as Name,
decode(c.InvoiceName,null,c.Zip,c.InvoiceZip) as Zip,
decode(c.InvoiceName,null,c.City,c.InvoiceCity) as City
<...>
from
Customers c
where
Accountnumber=:accountnumber
Another rarely-used but very applicable Oracle-specific function is NVL2:
select
nvl2(c.InvoiceName,c.InvoiceName,c.Name) as Name,
nvl2(c.InvoiceName,c.InvoiceZip,c.Zip) as Zip,
nvl2(c.InvoiceName,c.InvoiceCity,c.City) as City
<...>
from
Customers c
where
Accountnumber=:accountnumber
I think you need the COALESCE function to clean up your select statement. It will take the first non null value in the list.
select
coalesce(c.InvoiceName, c.Name) Name,
coalesce(c.InvoiceZip, c.Zip) Zip,
coalesce(c.InvoiceCity, c.City) City
EDIT: By shahkalpesh
I am assuming that fields with Invoice in it will be NULL, when InvoiceName is NULL.
select
coalesce(c.InvoiceName, c.Name) Name,
coalesce(c.InvoiceName, c.Zip, c.InvoiceZip) Zip,
coalesce(c.InvoiceName, c.City, c.InvoiceCity) City